From 486d0fd4fd52bae7921b75546495e4a853d107f6 Mon Sep 17 00:00:00 2001 From: "Silas S. Brown" <ssb22@cam.ac.uk> Date: Fri, 20 Nov 2009 10:50:10 +0000 Subject: [PATCH] Gradint update git-svn-id: http://svn.code.sf.net/p/e-guidedog/code/ssb22/gradint@447 29193198-4895-4776-b068-10539e920549 --- gradint-build/Makefile | 2 +- gradint-build/src/booktime.py | 2 +- gradint-build/src/filescan.py | 2 +- gradint-build/src/frontend.py | 24 +++++++------ gradint-build/src/lessonplan.py | 2 +- gradint-build/src/loop.py | 2 +- gradint-build/src/makeevent.py | 4 +-- gradint-build/src/play.py | 8 ++--- gradint-build/src/recording.py | 2 +- gradint-build/src/sequence.py | 2 +- gradint-build/src/synth.py | 64 +++++++++++++++++++++------------ gradint-build/src/system.py | 6 ++-- gradint-build/src/top.py | 2 +- gradint-build/thindown.py | 2 +- 14 files changed, 74 insertions(+), 50 deletions(-) diff --git a/gradint-build/Makefile b/gradint-build/Makefile index 45dd9f1..6a358ad 100644 --- a/gradint-build/Makefile +++ b/gradint-build/Makefile @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/booktime.py b/gradint-build/src/booktime.py index 02a8225..f883974 100644 --- a/gradint-build/src/booktime.py +++ b/gradint-build/src/booktime.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/filescan.py b/gradint-build/src/filescan.py index 4ec6761..eb157fd 100644 --- a/gradint-build/src/filescan.py +++ b/gradint-build/src/filescan.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/frontend.py b/gradint-build/src/frontend.py index ffe48e1..5a273cd 100644 --- a/gradint-build/src/frontend.py +++ b/gradint-build/src/frontend.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -102,8 +102,7 @@ def startBrowser(url): # true if success except: webbrowser=0 if webbrowser: g=webbrowser.get() - if g and (winCEsound or macsound or (hasattr(g,"background") and g.background) or (hasattr(webbrowser,"Konqueror") and g.__class__==webbrowser.Konqueror)): - # (TODO "background" should cover Galeon, Mozilla, Netscape, Opera, but is this always the case?) + if g and (winCEsound or macsound or (hasattr(g,"background") and g.background) or (hasattr(webbrowser,"BackgroundBrowser") and g.__class__==webbrowser.BackgroundBrowser) or (hasattr(webbrowser,"Konqueror") and g.__class__==webbrowser.Konqueror)): return g.open_new(url) # else don't risk it - it might be text-mode and unsuitable for multitask-with-gradint if winsound: return not os.system('start "%ProgramFiles%\\Internet Explorer\\iexplore.exe" '+url) # use os.system not system here (don't know why but system() doesn't always work for IE) @@ -232,7 +231,7 @@ def make_output_row(parent): Tkinter.Radiobutton(row, text=u" "+variant+u" ", variable=app.scriptVariant, value=str(count), indicatoron=0).pack({"side":"left"}) count += 1 app.scriptVariant.set(str(scriptVariants.get(firstLanguage,0))) - if not got_program("sox"): return row # can't do any file output without sox + if not gotSox: return row # can't do any file output without sox if not hasattr(app,"outputTo"): app.outputTo = Tkinter.StringVar(app) # NB app not parent (as parent is no longer app) if not row: row = Tkinter.Frame(parent) @@ -920,7 +919,7 @@ def startTk(): else: self.updateLanguageLabels() if hasattr(self,"vocabList"): del self.vocabList # it will need to be re-made now def updateLanguageLabels(self): - # TODO things like "To" and "Speaker" need updating dynamically with localise() as well, otherwise will be localised only on restart + # TODO things like "To" and "Speaker" need updating dynamically with localise() as well, otherwise will be localised only on restart (unless the old or new lang has variants, in which case it will be repainted anyway above) self.Label1["text"] = (localise("Word in %s") % localise(secondLanguage))+":" if winsound or mingw32 or cygwin: self.Label1["text"] += "\n(" + localise("press Control-V to paste")+")" elif macsound: self.Label1["text"] += "\n("+localise("press Apple-V to paste")+")" @@ -1536,18 +1535,23 @@ def rest_of_main(): del tbObj try: import traceback except: - w += "Cannot import traceback" + w += "Cannot import traceback\n" traceback = None if traceback and useTK: traceback.print_exc() # BEFORE waitOnMessage, in case Tk is stuck (hopefully the terminal is visible) try: if not soundCollector and get_synth_if_possible("en",0): synth_event("en","Error in graddint program.").play() # if possible, give some audio indication of the error (double D to try to force correct pronunciation if not eSpeak, e.g. S60) except: pass - waitOnMessage(w) - try: tracebackFile=open("last-gradint-error"+extsep+"txt","w") # TODO document this in the user message? + try: tracebackFile=open("last-gradint-error"+extsep+"txt","w") except: tracebackFile=None - if tracebackFile: tracebackFile.write(w+"\n") + if tracebackFile: + try: + tracebackFile.write(w+"\n") + if traceback: traceback.print_exc(None,tracebackFile) + tracebackFile.close() + if traceback: w += "Details have been written to "+os.getcwd()+os.sep+"last-gradint-error"+extsep+"txt" # do this only if there's a traceback, otherwise little point + except: pass + waitOnMessage(w.strip()) if traceback and not useTK: traceback.print_exc() - if traceback and tracebackFile: traceback.print_exc(None,tracebackFile) exitStatus = 1 if appuifw: raw_input() # so traceback stays visible # It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits. So: diff --git a/gradint-build/src/lessonplan.py b/gradint-build/src/lessonplan.py index d0be9ba..b9cead4 100644 --- a/gradint-build/src/lessonplan.py +++ b/gradint-build/src/lessonplan.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/loop.py b/gradint-build/src/loop.py index 523eb5d..0fe25a1 100644 --- a/gradint-build/src/loop.py +++ b/gradint-build/src/loop.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/makeevent.py b/gradint-build/src/makeevent.py index 2c44818..8f949e9 100644 --- a/gradint-build/src/makeevent.py +++ b/gradint-build/src/makeevent.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -364,7 +364,7 @@ def simplified_header(fname): if h: return h[:3]+h[4:] def optimise_partial_playing_list(ceList): # similar to above, but returns a ShellEvent for a list of ce's that are to be separated by short pauses, or None if can't do this optimisation. This is because sox on NSLU2's etc has too much latency for the short pauses. - if (soundCollector and not saveLesson) or not playProgram=="aplay": return + if (soundCollector and not saveLesson) or not playProgram=="aplay" or not gotSox: return format = None ; l = [] ; theLen = 0 for ce in ceList: for e in ce.eventList: diff --git a/gradint-build/src/play.py b/gradint-build/src/play.py index 4ba16a6..b3ed6b2 100644 --- a/gradint-build/src/play.py +++ b/gradint-build/src/play.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -114,7 +114,7 @@ if winsound or mingw32: # TODO now that we (usually) have tkSnack bundled with the Windows version, can we try that also (with file=) before sndrec32? if fileExists(os.environ.get("windir","C:\\Windows")+"\\system32\\sndrec32.exe"): playProgram = "start /min sndrec32 /play /close" # TODO could also use ShellExecute or some other utility to make it completely hidden elif unix and not macsound: - sox_type = "-t ossdsp -s "+sox_16bit # (always specify 16-bit because if we're adjusting the volume of 8-bit wav's then we could lose too many bits in the adjustment unless we first convert to 16-bit) # TODO run sox -h to see if that device is actually supported (" ossdsp" will be in the output) + sox_type = "-t ossdsp -s "+sox_16bit # (we will check that sox can do ossdsp below) (always specify 16-bit because if we're adjusting the volume of 8-bit wav's then we could lose too many bits in the adjustment unless we first convert to 16-bit) if not soundVolume==1: sox_effect=" vol "+str(soundVolume) if sox_effect and not gotSox: show_warning("Warning: trying to adjust soundVolume when 'sox' is not on the system might not work") @@ -126,7 +126,7 @@ elif unix and not macsound: for dsp in dsps_to_check: if fileExists_stat(dsp): oss_sound_device = dsp - if dsp=="/dev/audio": sox_type="-t sunau -s "+sox_16bit # TODO run sox -h to see if that device is actually supported (" sunau" will be in the output) + if dsp=="/dev/audio": sox_type="-t sunau -s "+sox_16bit break if sox_formats.find("-q")>-1: sox_type="-q "+sox_type # Try to find playProgram (and maybe mpg123, for use if no madplay or mp3-playing playProgram) @@ -246,7 +246,7 @@ class SampleEvent(Event): except RuntimeError: return 1 elif macsound: return system("qtplay \"%s\"" % (self.file,)) elif riscos_sound: - if fileType=="mp3": file=theMp3FileCache.decode_mp3_to_tmpfile(self.file) # (TODO find a RISC OS program that can play the MP3's directly?) + if fileType=="mp3": file=theMp3FileCache.decode_mp3_to_tmpfile(self.file) # (TODO find a RISC OS program that can play the MP3s directly?) else: file=self.file system("PlayIt_Play \"%s\"" % (file,)) elif playProgram.find('sndrec32')>-1: diff --git a/gradint-build/src/recording.py b/gradint-build/src/recording.py index bbf859f..78bd627 100644 --- a/gradint-build/src/recording.py +++ b/gradint-build/src/recording.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/sequence.py b/gradint-build/src/sequence.py index 68e9a9b..49c173c 100644 --- a/gradint-build/src/sequence.py +++ b/gradint-build/src/sequence.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or diff --git a/gradint-build/src/synth.py b/gradint-build/src/synth.py index 3759b23..ad9b75c 100644 --- a/gradint-build/src/synth.py +++ b/gradint-build/src/synth.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -75,7 +75,7 @@ class OSXSynth_Say(Synth): def aiff2wav(fname): if not system("sox \"%s\" \"%s\"" % (fname,fname[:-4]+"wav")): - # good, we can convert it to wav + # good, we converted it to wav os.remove(fname) fname=fname[:-4]+"wav" # else just return aiff and hope for the best (TODO won't work with cache-synth) @@ -492,32 +492,44 @@ class ESpeakSynth(Synth): if not (winsound or macsound): show_warning("Warning: eSpeak's transliterate returned wrong number of items (%d instead of %d). Falling back to separate runs for each item (slower)." % (len(data),len(indexList))) return None for index,dat in zip(indexList,data): - en_words=[] # any en words that espeak found embedded in the text - r=[] ; lastWasBlank=False ; ignore_next_translate = 0 + en_words={} # any en words that espeak found embedded in the text + r=[] ; lastWasBlank=False delete_last_r_if_blank = 0 + thisgroup_max_priority,thisgroup_enWord_priority = 0.5,0 for l in dat.strip(wsp).split("\n"): + if lang=="zh": # sort out en_words + lWords = l.split() + if lWords: int0 = intor0(lWords[0]) + else: int0 = 0 + if int0: + if int0 > thisgroup_max_priority: + thisgroup_max_priority = int0 + if lWords[-1]=="[_^_]": thisgroup_enWord_priority = int0 # so far it looks like this is going to be an English word + else: # a split between the groups + if thisgroup_enWord_priority == thisgroup_max_priority: # the choice with the highest priority was the one containing the [_^_] to put the word into English + en_words[r[-1]]=1 + thisgroup_max_priority,thisgroup_enWord_priority = 0.5,0 + # end of sort out en_words if lang=="zh" and not lastWasBlank and r and (l.startswith("Replace") or l.startswith("Translate") or l.startswith("Found")): r[-1]+="," # (because not-blank is probably the line of phonemes) if delete_last_r_if_blank and not l: r=r[:-1] # "Translate" followed by blank line is probably corner-brackets or something; don't want that confusing the transliteration (especially if it's for partials) + delete_last_r_if_blank = 0 foundLetter=0 if l.startswith("Translate "): toAppend=l[l.index("'")+1:-1].replace("\xc3\xbc","v") - if lang=="zh" and ((not ignore_next_translate) or not r[-1]==toAppend): # 'or not' condition added because sometimes a "[_^_]" rule appears in the -X output as being considered but is not actually taken, e.g. "de2zhao2 da4di4". TODO if this happens when the word really is repeated in the input, this code will erase the repetition. + if lang=="zh" and not (toAppend in en_words and r and toAppend==r[-1]): + # TODO what about partial English words? e.g. try "kao3 testing" - translate 'testing' results in a translate of 'test' also (which assumes it's already in en mode), resulting in a spurious word "test" added to the text box; not sure how to pick this up without parsing the original text and comparing with the Replace rules that occurred r.append(toAppend) delete_last_r_if_blank = 1 - else: - en_words.append(toAppend) - delete_last_r_if_blank = 0 - ignore_next_translate = 0 + else: # lang=="zhy", or it's a duplicate of a word we already know to be in en_words + en_words[toAppend]=1 # make sure it's in there else: # not Translate - delete_last_r_if_blank = 0 - if lang=="zh" and forPartials and l.startswith("Found: ") and l[8]==" " and "a"<=l[7]<="z": # an alphabetical letter - we can say this as a Chinese letter and it should be compatible with more partials-based synths. But DON'T do this if going to give it to a unit-selection synth - 'me1' and 'ne1' don't have hanzi and some synths will have difficulty saying them. - r.append("a1 bo1 ci1 de1 e1 fou1 ge1 he1 yi1 ji1 ke1 le1 me1 ne1 wo1 po1 qi1 ri4 si1 te4 yu1 wei4 wu1 xi1 ye1 zi1".split()[ord(l[7])-ord('a')]) + if lang=="zh" and l.startswith("Found: ") and l[8]==" " and "a"<=l[7]<="z": # an alphabetical letter - we can say this as a Chinese letter and it should be compatible with more partials-based synths. But DON'T do this if going to give it to a unit-selection synth - 'me1' and 'ne1' don't have hanzi and some synths will have difficulty saying them. + if forPartials: r.append("a1 bo1 ci1 de1 e1 fou1 ge1 he1 yi1 ji1 ke1 le1 me1 ne1 wo1 po1 qi1 ri4 si1 te4 yu1 wei4 wu1 xi1 ye1 zi1".split()[ord(l[7])-ord('a')]) + else: r.append(l[7]) foundLetter = 1 - elif lang=="zh" and lastWasBlank and l.find("[_^_]")>-1: en_words.append(r[-1]) # hack to make sure all en_words are detected elif not lang=="zh" and l.startswith("Found: ") and ord(l[7])>127: r.append(l[l.index("[")+1:l.index("]")]) lastWasBlank=(l.startswith("Replace") or not l or foundLetter) # (take 'Replace' lines as blank, so 'Translate' doesn't add a second comma. ditto letters thing.) - if l.find("[_^_]")>-1: ignore_next_translate=1 # because it will be a duplicate (an en word in the zh) if lang=="zh": retList[index]=fix_pinyin(" ".join(r),en_words) else: retList[index]=" ".join(r) return retList @@ -601,7 +613,7 @@ def fix_pinyin(pinyin,en_words): def stripPunc(w): i=0 ; j=len(w) ; w=w.lower() while i<len(w) and not 'a'<=w[i]<='z': i+=1 - while j>1 and not 'a'<=w[j-1]<='z': j-=1 + while j>1 and not ('a'<=w[j-1]<='z' or '1'<w[j-1]<='5'): j-=1 return w[i:j] for w in pinyin.split(): if stripPunc(w) in en_words: ret.append(w) @@ -679,16 +691,24 @@ def fix_compatibility(utext): # convert 'compatibility full-width' characters to # Older versions of eSpeak output WAVs with 0 length and can't be piped through aplay espeak_pipe_through = "" # or "--stdout|..." (NOT on Windows) -done_espeak_recent = None -def espeak_is_recent(): - global done_espeak_recent - if done_espeak_recent==None: done_espeak_recent=('zh' in ESpeakSynth().languages) - return done_espeak_recent +def espeak_stdout_works(): + assert unix, "espeak_stdout_works should be called only if unix" + # recent enough for --stdout to work. Don't be tempted to look for "zh" in languages + # because espeak 1.31 shipped with zh (broken) and a broken --stdout (length marked as 0) + versionLine = filter(lambda x:x,os.popen(ESpeakSynth().program+" --help 2>&1").read().split("\n"))[0] + versionLine = versionLine[versionLine.find(":")+1:].strip() + versionLine = versionLine[:versionLine.find(" ")] + versionLine = (versionLine+".")[:versionLine.find(".",versionLine.find(".")+1)] # if x.y.z just have x.y + try: return (float(versionLine)>=1.32) + except ValueError: return False +def espeak_volume_ok(): + # if has "zh", should be recent enough + return "zh" in ESpeakSynth().languages if unix and not macsound and not (oss_sound_device=="/dev/sound/dsp" or oss_sound_device=="/dev/dsp"): - if playProgram=="aplay" and espeak_is_recent(): espeak_pipe_through="--stdout|aplay -q" # e.g. NSLU2 + if playProgram=="aplay" and espeak_stdout_works(): espeak_pipe_through="--stdout|aplay -q" # e.g. NSLU2 else: del ESpeakSynth.play # because we have no way of sending it to the alternative device, so do it via a file if hasattr(FliteSynth,"play"): del FliteSynth.play -if hasattr(ESpeakSynth,"play") and (soundVolume<0.04 or (soundVolume<0.1 and not espeak_is_recent()) or soundVolume>2): del ESpeakSynth.play # old versions of espeak are not very good at less than 10% volume, so generate offline and use sox +if hasattr(ESpeakSynth,"play") and (soundVolume<0.04 or (soundVolume<0.1 and not espeak_volume_ok()) or soundVolume>2): del ESpeakSynth.play # old versions of espeak are not very good at less than 10% volume, so generate offline and use sox class FestivalSynth(Synth): def __init__(self): Synth.__init__(self) diff --git a/gradint-build/src/system.py b/gradint-build/src/system.py index 7a59fe3..c22f115 100644 --- a/gradint-build/src/system.py +++ b/gradint-build/src/system.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or @@ -285,7 +285,7 @@ if winCEsound and __name__=="__main__": Tk_might_display_wrong_hanzi = wrong_hanzi_message = "" if macsound: if sys.version.startswith("2.3.5"): Tk_might_display_wrong_hanzi="10.4" - elif sys.version.startswith("2.5.1"): + elif sys.version[:5] >= "2.5.1": # 10.5+ f="/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-dynload/_tkinter.so" if fileExists(f): # we might be able to patch this one up if not isDirectory("Frameworks") and fileExists("Frameworks.tbz"): os.system("tar -jxvf Frameworks.tbz && rm Frameworks.tbz && chmod -R +w Frameworks") @@ -296,7 +296,7 @@ if macsound: _tkinter.TK_VERSION = _tkinter.TCL_VERSION = "8.6" else: Tk_might_display_wrong_hanzi="10.5" if Tk_might_display_wrong_hanzi: wrong_hanzi_message = "NB: In Mac OS "+Tk_might_display_wrong_hanzi+", Chinese\ncan display wrongly here." # so they don't panic when it does -# TODO what about OS X 10.6+ ? +# TODO can we test on OS X 10.6+ ? is the above workaround still needed? # Handle keeping progress file and temp directories etc if we're running from a live CD # (and if the live CD has just been copied to the hard disk, look in the old progress file locations also) diff --git a/gradint-build/src/top.py b/gradint-build/src/top.py index 2576e64..4b9c06f 100644 --- a/gradint-build/src/top.py +++ b/gradint-build/src/top.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -program_name = "gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+." +program_name = "gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+." # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/gradint-build/thindown.py b/gradint-build/thindown.py index 3b1ccb8..5c7045b 100644 --- a/gradint-build/thindown.py +++ b/gradint-build/thindown.py @@ -1,5 +1,5 @@ # This file is part of the source code of -# gradint v0.993 (c) 2002-2009 Silas S. Brown. GPL v3+. +# gradint v0.9931 (c) 2002-2009 Silas S. Brown. GPL v3+. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or -- GitLab