# -*- coding: ISO-8859-1 -*-
""" capellaScript -- Copyright (c) 2004 Hartmut Ring
>>> capella-Braille-Export (Prototyp)

    Dieses Skript soll blinden capella-Anwendern über eine Braille-Zeile
    einen schnellen Überblick über eine capella-Partitur geben.||

    Das Skript implementiert bisher nur einen Teil der Braille-Musiknotation.|
    Vielleicht haben Sie Lust, dieses Skript weiter zu entwickeln.
    Sie würden damit vielen blinden Musikern helfen.|
    Für Hilfe zur Braille-Notation können Sie sich an
    Herrn Antonio Michienzi (a.michienzi@t-online.de) wenden.
<<<
"""

"""
Anpassungen von Paul Villiger (villpaul@swissonline.ch)
27.12.04 - Absturz bei b-Tonarten korrigiert
         - Punktierte Noten korrigiert
         - Mehrtaktpausen, bei > 9 werden Pausen unterteilt ???
         - Tonartwechsel innerhalb Zeile nach C-Dur
11.02.05 - Fehler bei Taktwechsel innerhalb Notenzeile
14.02.05 - Vorzeichen; Einschränkung: Folgende Vorzeichen
           innerhalb eines Taktes werden noch nicht unterdrückt.
20.10.05 - Bindebogen mit C und <B .. >; implementiert
         - Haltebogen mit "C implementiert
21.10.05 - 8tel- Noten klein
         - Ganze Noten {} durch [] ersetzt ??
         - Taktstriche und Wiederholungen
         - Vor Tonart/Takt innerhalb einer Zeile wird eine doppelter Taktstrich gezeichnet, wenn nicht vorhanden ??
22.10.05 - Alle Buchstaben in Kleinschrift
         - Vorzeichen auch bei C-Dur
         - Neue Zeichen bei Tonarten mit mehr als 3 B/3
         - Leerschlag nach Taktwechsel innerhalb Notenzeile
         - kurzer Phrasierungsbogen (punktierter Bindebogen)
         - Oktavierung eine Stufe höher
         - C- und allaBreve-Zeichen
         - Dynamikzeichen
         - Artikulationszeichen
23.10.05 - Zwischen Dynamik und Artikulation steht ein Punkt 3
27.10.05 - 1/256 Noten
         - Wertscheidungszeichen (WSZ) zwischen Noten
         - Wertscheidungszeichen am Zeilenanfang
         - Kastennoten
28.10.05 - Oktavierungszeichen zwischen WSZ und Note
         - Fehler mit Punkt 3 Zeichen
29.10.05 - G-F-C-Schlüssel mit Oktavierung
30.10.05 - Vorzeichenwiederholung in einem Takt funktioniert
31.10.05 - Korrektur Vorzeichenwiederholung
01.11.05 - Behandlung von Tuplets
05.11.05 - Intervalle (Akkorde)
         - Voltenklammern
         - Crescendo, Decrescendo
         - diverse Musiksymbole: segno, D.S., D.C., coda, pedal, fermate, auf/abstrich
06.11.05 - Voltenklammern für alle Nummern
         - Haltebogen für Akkorde $c
         - automatischer Taktstrich (' ') vor Schlüssel
         - Fermate nach Note
         - Voltazeichen mit @#
         - Pedalzeichenende vor Dynamikzeichen und wedge
         - Fehler bei Akkorden mit Oktav
         - Punkt 3 Behandlung nach Dynamikzeichen und Voltenklammern
08.11.05 - Absturz bei Punkt 3 behoben
09.11.05 - Bei Dynamikzeichen de-cresc Ende weglassen
         - Liedtext erste Implementation
13.11.05 - Triller, Doppelschlag und Mordent
14.11.05 - tr wird zu @tr
16.11.05 - '_'  Erweiterung bei Liedtext
         - Atemzeichen
         - Instrumenten-/Stimmenbezeichnung
         - Texte
19.11.05 - Ziffern in Texten und Stimmenbezeichnungen werden nach Braille konvertiert
         - Fehler bei Alteration ohne Vorzeichen
         - römische Bezifferung im Text
20.11.05 - de/cresc vor Dynamikzeichen
21.11.05 - Ziffern in Liedtextnummern werden nach Braille konvertiert
         - nach de/cresc Oktavzeichen
         - nach Text Punkt 3 wenn notwendig
22.11.05 - Vor Wortzeichen '@' ist kein P3 notwendig
         - Titel aus Pageobjkten
24.11.05 - Pageobjekte werden sortiert von oben nach unten
15.12.05 - Transponierbare Symbole werden nicht als Text behandelt
16.12.05 - Behandlung Ausgabedirectory
19.12.05 - falsche Leerzeichen nach Ganztaktpausen
21.12.05 - Mehrtaktpausen mit Braillezahlen
19.01.06 - Falsche Zeitberechnung bei Ganz- und Mehrtaktpausen

"""

test = False # Ausgabe in Braillenotation
# test = True # Ausgabe im Klartext zur besseren Lesbarkeit beim Testen

import caplib.capDOM, tempfile, sys, new, string, os
from xml.dom.minidom import NodeList, Node, Element

#-----------------------------------------------------------------------
# verwendete Braille-Zeichen:

digits  = 'jabcdefghi'
digits2 = '),;:/?+=(*'
def meterSign(p,q):
    s = '#'
    for c in p:
        s += digits[int(c)]
    for c in q:
        s += digits2[int(c)]
    return s

wholeNotes   = 'yz&%[^]m'  # Zeichen für c, d, e, f, g, a, h, Pause
halfNotes    = 'nopqrstu'
quarterNotes = '456789wv'
eighthNotes  = 'defghijx'

octaves = ('""', '"', '>', '_', '!', '$', '<', "'") # Subkontraoktave bis dreigestrichene Oktave

# Die Oktave wird geschrieben:
# (a) vor dem ersten Ton einer Notenzeile
# (b) falls Schritt von letzter Note mindestens Sexte ist
# (c) falls Schritt von letzter Note Quarte oder Quinte ist
#     und die Oktave (c...h) gewechselt wird

clefG        = '@\\l'     # Violinschlüssel
clefF        = '@#l'      # Bass-Schlüssel
clefC        = '@0l'       # Alt-Schlüssel
clefBassa    = '#('        # Oktavierung nach unten
clefAlta     = '#h'        # Oktavierung nach oben
numberSign   = '#'         # um ABC... als 123... zu verwenden
symbolC      = '$c'        # C
allaBreve    = '_c'        # alla Breve
sharp        = '3'         # Kreuz
flat         = '2'         # Be
natural      = '1'         # Auflösungszeichen
sharps       = ['','3','33','333','#d3','#e3','#f3','#g3']
flats        = ['','2','22','222','#d2','#e2','#f2','#g2']
naturals     = ['','1','11','111','#d1','#e1','#f1','#g1']
tieSign      = '"c'        # Haltebogen
allTieSign   = '$c'        # Haltebogen bei Akkorden (alle Töne gleich)
slurShort    = 'c'         # kurzer Bindebogen
slurBegin    = '<b'        # langer Bindebogen Anfang
slurEnd      = '>;'        # langer Bindebogen Ende
slurPhrase   = '_c'        # punktierter Bindebogen kurz
crescBegin   = '@c'
crescEnd     = '@:'
decrescBegin = '@d'
decrescEnd   = '@/'
undefined    = ''
barlineEnd      = '2k'
barlineDouble   = '2k.'
barlineRepBegin = '2='
barlineRepEnd   = '2;'
barSeparation   = '! '     # Takttrennungspunkt
prefix256       = '<2.'    # Ankündigung 256tel
valueSepCommon  = '2,'     # Wertscheidungszeichen allgemein
valueSepGreater = '>2,'    # Wertscheidungszeichen Gross
valueSepSmaller = "'2,"    # Wertscheidungszeichen Klein
boxSign         = '>c'     # Kastenzeichen für Kastennoten
lyricSign       = '<;'
extenderSign    = '-'      # _ bei Lied
hyphenSign      = '-'      # Liedtext Silbentrennung

trSign          = '@tr'    # tr
trillSign       = '+'      # Trillerschlange ohne tr
shortTrillSign  = '!+'     # kurzer Pralltriller
longTrillSign   = '<+'     # langer Pralltriller

shortMordentSign      = '!+L'    # kurzer Mordent
longMordentSign       = '<+L'    # langer Mordent
continuationStartSign = '..'     # Weitergeltungslinie Anfang
continuationEndSign   = '..'     # Weitergeltungslinie Ende
dSchlagSign     = '/'      # Doppelschlag
aboveSign       = "'"      # Doppelschlag ist über Note ( '/ )
reverseSign     = 'L'      # Umgekehrter Doppelschlag ( /L oder '/L )

breathSign      = '@,'     # Atemzeichen

voltaSignBegin  = '@#'
voltaSignContinue = '-'

p3Sign          = '.'      # Punkt 3
withoutPoint123 = ' !"$\'<>_'

intervalSigns   = '-\\0#*):-'  # Prime (Oktave), Sekunde, Terz, Quart Quint, Sext, Septime, Oktave

dynamicSigns    = {'f':'@f',        # f
                   'g':'@ff',       # ff
                   'h':'@fff',      # fff
                   'i':'@mp',       # mp
                   'j':'@mf',       # mf
                   'p':'@p',        # p
                   'q':'@pp',       # pp
                   'r':'@ppp',      # ppp
                   's':'@sf',       # sf
                   'z':'@sfz',      # sfz
                   '{':'@fz',       # fz
                   '|':'@fp',       # fp
                   }

segnoSign       = {'$':' 0 ',       # Segno 2
                   'y':' 0 '}       # Segno
codaSign        = {'n':' 0l ',      # Coda gross
                   'o':' 0l '}      # Coda klein
dsSign          = {'d':'@ds.'}      # D.S.
dcSign          = {'e':'@dc.'}      # D.C.
fermateSign     = {'k':'2L',        # Fermate unten
                   'u':'2L'}        # Fermate oben
pedalDownSign   = {'a':'2c'}        # Pedal Anfang
pedalUpSign     = {'b':'1c'}        # Pedal Ende
bowSign         = {'Y':'2b',        # Abstrich
                   'Z':'2.'}        # Aufstrich

                   

articulationChars = { chr(200):'tenuto',            # Tenuto    
                      chr(201):'staccatissimo',     # Staccatissimo
                      chr(202):'normalAccent',      # normaler Akzent
                      chr(203):'strongAccent',      # starker Akzent
                      chr(204):'weakBeat',          # weicher Schlag
                      chr(205):'strongBeat',        # harter Schlag
                      chr(206):'staccatissimo',     # unten
                      chr(207):'strongBeat',        # harter Schlag unten
                      chr(180) :'staccato'          # staccato (Fingersymbol)
                    }

# Die Reihenfolge wie die Artikulationszeichen gesetzt werden
articulationsList = ['staccato', 'tenuto', 'staccatissimo', 'normalAccent', 'strongAccent', 'weakBeat', 'strongBeat']

staccato = '('
tenuto   = '_('
staccatissimo = "'("
normalAccent = '$('
mezzostaccato = '!('    # Punkt und Querstrich
strongAccent = '<('
weakBeat = ''           # ??
strongBeat = ''         # ??

articulation2Braille = {'staccato':staccato,
                        'tenuto':tenuto,
                        'staccatissimo':staccatissimo,
                        'normalAccent':normalAccent,
                        'strongAccent':strongAccent,
                        'weakBeat':weakBeat,
                        'strongBeat':strongBeat}

if test: # zur besseren Lesbarkeit beim Testen
    wholeNotes   = ['<C1>','<D1>','<E1>','<F1>','<G1>','<A1>','<B1>','<P1>']  # Zeichen für c, d, e, f, g, a, h, Pause
    halfNotes    = ['<C2>','<D2>','<E2>','<F2>','<G2>','<A2>','<B2>','<P2>']
    quarterNotes = ['<C4>','<D4>','<E4>','<F4>','<G4>','<A4>','<B4>','<P4>']
    eighthNotes  = ['<C8>','<D8>','<E8>','<F8>','<G8>','<A8>','<B8>','<P8>']
    octaves = ('<O2>', '<O3>', '<O4>', '<O5>', '<O6>', '<O7>', '<O8>', "<O9>") # Subkontraoktave bis dreigestrichene Oktave

    trebleClef   = '<SG> '               # Violinschlüssel
    bassClef     = '<SF> '               # Bass-Schlüssel
    tieSign      = '<Haltebogen>'        # Haltebogen
    slurShort    = '<Bindebogen>'        # kurzer Bindebogen
    slurBegin    = '<Bindebogen Anfang>' # langer Bindebogen Anfang
    slurEnd      = '<Bindebogen Ende>'   # langer Bindebogen Ende
    undefined    = '<undefined>'

# In dieser Version werden die Zeilen eines Systems nicht taktgerecht
# untereinander ausgerichtet!


#-----------------------------------------------------------------------
# Hilfsfunktionen

def latin1(u):
    return u.encode('Latin-1')

class ScriptOptions (object):
    def __init__(self):
        dir, file = os.path.split(sys.argv[0])
        fileName = 'braille-view.opt'
        self._optionsFile = os.path.join(dir, fileName)

    def get(self):
        opt = dict()
        try:
            f = file(self._optionsFile)
            l = f.readlines()
            f.close()
            for x in l:
                a = [s.strip() for s in x.split('=')] 
                if len(a) == 2:
                    opt[a[0]] = a[1]
        except: pass
        return opt

    def set(self, d):
        f = file(self._optionsFile, 'w')
        for i in d:
            f.write('%s = %s\n' % (i, str(d[i])))
        f.flush()            
        f.close()

options = ScriptOptions()
opt = options.get()     # opt = dict()


def gotoChild(self, name, new=False):
    newEl = None
    if new:
        pass
    else:
        for child in self.childNodes:
            if child.nodeType == child.ELEMENT_NODE and child.tagName == name:
                newEl = child
                break
    if newEl == None:
        newEl = doc.createElement(name)
        self.appendChild(newEl)
    return newEl

Node.gotoChild = new.instancemethod(gotoChild,None,Node)

def getChildNodes(self):
    childs = []
    for child in self.childNodes:
        if child.nodeType == child.ELEMENT_NODE:
            childs.append(child)
    return childs
Node.getChildNodes = new.instancemethod(getChildNodes,None,Node)

def brailleNumToChar(c):
    return chr(ord('A') + ord(c) - ord('0'))

def convertDigitsToBraille(s):
    returnString = ''
    digitSign = False
    for c in s:
        if c in string.digits:
            if not digitSign:
                returnString += numberSign
                digitSign = True
            returnString += digits[int(c)]
        else:
            returnString += c
            digitSign = False
    return returnString

def meterToRational(s):
    if s in ('allaBreve', 'C'):
        s = '4/4'
    elif s == 'infinite':
        s = '1000/1'
    try:
        t = s.split('/')
        p = t[0].strip()
        q = t[1].strip()
        return Rational(int(p),int(q))
    except:
        messageBox('Fehler', '[%s]' % s);

class RingStorage(int):
    def __init__(self):
        self.ring = [None] * 64
        for i in range(0,64):
            self.ring[i] = []
        self.pointer = 0

    def add(self, offset, el):
        poi = (self.pointer + offset) % 64
        self.ring[poi].append(el)

    def isfilled(self, offset=0):
        return self.ring[(self.pointer + offset) % 64] <> []

    def getFirst(self, offset=0):
        poi = (self.pointer + offset) % 64
        l = self.ring[poi]
        r = None
        if len(l) > 0:
            self.ring[poi] = l[1:]
            r = l[0]
        return r

    def getLast(self, offset=0):
        poi = (self.pointer + offset) % 64
        l = self.ring[poi]
        r = None
        if len(l) > 0:
            self.ring[poi] = l[:len(l)-1]
            r = l[len(l)]
        return r

    def next(self):
        self.pointer = (self.pointer + 1) % 64
            
            
slurStorage = RingStorage()  # Bindebogen
wedgeStorage = RingStorage() # Cresc. Decresc. Zeichen
trillStorage = RingStorage() # Triller über mehrere Noten

#---------------------------------------------------------------

class BrailleExport (caplib.capDOM.ScoreExport):
    def writeBraille(self, s):
        if self.printP3:
            if len(s) > 0:
                # vor einem Wortzeichen ist kein Punkt notwendig
                if s[0] == '@':
                    pass
                # Punkt muss vor Zeichen folgen, welche nicht Punkt 1, 2 oder 3 enthalten
                elif s[0] not in withoutPoint123:
                    self.f.write(p3Sign)
                self.printP3 = False
        self.f.write(s)

    def startScore(self, score):
        self.printP3 = False

        titleFound = False
        pageTexts = []
        for pageObject in score.getElementsByTagName('pageObjects'):
            for text in pageObject.getElementsByTagName('text'):
                y = float(text.getAttribute('y'))
                for content in text.getElementsByTagName('content'):
                    cont = latin1(content.firstChild.nodeValue)
                    cont = convertDigitsToBraille(cont)
                    cont = string.replace(cont, '\n', ' ')
                    pageTexts.append([y, cont])

        pageTexts.sort()
        for t in pageTexts:
            self.writeBraille(' ' * 10)
            self.writeBraille(t[1])
            self.writeBraille('\n')
            titleFound = True
            
        if titleFound:
            self.writeBraille('\n')
            
        self.staffNames = {}
        for staffLayout in score.getElementsByTagName('staffLayout'):
            description = latin1(staffLayout.getAttribute('description'))
            for instrument in staffLayout.getElementsByTagName('instrument'):
                name = latin1(instrument.getAttribute('name'))
                abbrev = latin1(instrument.getAttribute('abbrev'))
            name = convertDigitsToBraille(name)
            abbrev = convertDigitsToBraille(abbrev)
            self.staffNames[description] = [name,abbrev]
        
        self.nSys = 0

    def finishScore(self, score):
        pass

    def startSystem(self, sys):
        self.nSys += 1
        self.nStave = 0
        self.instrNotation = sys.getAttribute('instrNotation')

    def startStaff(self, staff):
        self.nStave += 1
        self.nVoice = 0
        self.meter = meterToRational(staff.getAttribute('defaultTime'))
        self.staffLayout = latin1(staff.getAttribute('layout'))

    def finishSystem(self, sys):
        self.f.write('\n')

    def startVoice(self, vc):
        self.firstNote = True
        self.barline = True
        self.barSeparation = False
        self.printP3 = False
        self.triplet = False
        self.nVoice += 1
        
        name, abbrev = self.staffNames[self.staffLayout]
        if self.instrNotation == 'long':
            if name <> '':
                self.writeBraille(name + ' ')
        elif self.instrNotation == 'short':
            if abbrev <> '':
                self.writeBraille(abbrev + ' ')
            
        self.writeBraille('%d-%d-%d ' % (self.nSys, self.nStave, self.nVoice))
        self.lastPitch = -100  # am Anfang eine Oktavangabe erzwingen!
        self.time = Rational(0)
        self.fifths = 0
        self.lastBase = '0'
        self.octaveSign = ''
        self.pedalDown = ''
        self.pedalUp = ''
        self.alterationSigns = {}   # Zwischenspeicher für alle Vorzeichen innerhalb eines Taktes
        self.tupletTime = Rational(0)
        self.lyricDict = dict()
        self.breathStorage = ''     # Zwischenspeicher für Atemzeichen
        self.hasTie = False
        
    def finishVoice(self, voice):
        self.writeLyric()
        self.f.write('\n')

    def startChord(self, chord):
        self.nHeads = 0
        self.hasTie = False  # Irgend ein Kopf hat Haltebogen
        self.allTie = True   # Bleibt auf True wenn alle Köpfe Haltebogen haben
        self.pitches = []

    def handleClefSign(self, clefSign):
        clef = clefSign.getAttribute('clef')
        if   clef == 'treble': clef = 'G2'
        elif clef == 'bass' : clef = 'F4'
        elif clef == 'alto' : clef = 'C3'
        elif clef == 'tenor': clef = 'C4'

        self.clefSign = clef        
            
        if   'G' in clef:
            if clef[1] == '2' : self.writeBraille(clefG)
            else: self.writeBraille(clefG[0:2] + octaves[ int(clef[1]) ] + clefG[2])

        elif 'F' in clef:
            if clef[1] == '4' : self.writeBraille(clefF)
            else: self.writeBraille(clefF[0:2] + octaves[ int(clef[1]) ] + clefF[2])
            
        elif 'C' in clef:
            if clef[1] == '3' : self.writeBraille(clefC)
            else: self.writeBraille(clefC[0:2] + octaves[ int(clef[1]) ] + clefC[2])
        
        if   '-' in clef: self.writeBraille(clefBassa)
        elif '+' in clef: self.writeBraille(clefAlta)

        self.writeBraille(' ') # nach dem Schlüssel ein Leerzeichen         
        self.lastPitch = -100  # nach Schlüssel eine Oktavangabe erzwingen!

        
    def handleKeySign(self, keySign):
        # Vor die Tonart wird eine doppelter Taktstrich gezeichnet, wenn nicht vorhanden ??
        if not self.barline:
            self.writeBraille(barlineDouble)
            self.writeBraille(barSeparation)
            self.barline = True
        self.time = Rational(0)
        self.alterationSigns = {}
            
        fifths = int(keySign.getAttribute('fifths'))
        if fifths > 0:  self.writeBraille(sharps[fifths])
        elif fifths < 0: self.writeBraille(flats[-fifths])
        elif fifths == 0: self.writeBraille(naturals[abs(self.fifths)])
        self.firstNote = True
        self.triplet = False
        self.fifths = fifths
        self.lastPitch = -100  # nach Tonartwechsel eine Oktavangabe erzwingen!

    def handleTimeSign(self, timeSign):
        # Vor die Taktangabe wird eine doppelter Taktstrich gezeichnet, wenn nicht vorhanden ??
        if not self.barline:
            self.writeBraille(barlineDouble)
            self.writeBraille(barSeparation)
            self.barline = True
        self.time = Rational(0)
        self.alterationSigns = {}

        time = timeSign.getAttribute('time')
        if time == 'allaBreve':
            self.writeBraille(allaBreve)
            time = '2/2'
        elif time == 'C':
            self.writeBraille(symbolC)
            time = '4/4'
        else:
            t = time.split('/')
            self.writeBraille(meterSign(t[0], t[1]))
        self.meter = meterToRational(time)
        self.firstNote = True

    def handleBarline(self, n):    
        if n.tagName == 'barline':
            self.time = Rational(0)
            self.alterationSigns = {}
            self.lastBase = '0'
            self.barline = True
            type = n.getAttribute('type')
            if type == 'end':
                self.writeBraille(barlineEnd)
                self.barSeparation = True
            elif type == 'repEnd':
                self.writeBraille(barlineRepEnd)
                self.barSeparation = True
            elif type == 'double':
                self.writeBraille(barlineDouble)
                self.barSeparation = True
            elif type == 'repBegin':
                self.writeBraille(barlineRepBegin)
            elif type == 'repEndBegin':
                self.writeBraille(barlineRepEnd + barSeparation + barlineRepBegin)
            else:
                self.writeBraille(' ')

        elif self.time >= self.meter:
            # Leerschlag für Taktstrich
            while self.time >= self.meter:
                self.time -= self.meter
            self.alterationSigns = {}
            self.lastBase = '0'
            self.writeBraille(' ')


    def handleDynamics(self,noteObject):
        if noteObject.tagName in ['chord', 'rest']:
            for text in noteObject.getElementsByTagName('text'):
                for font in text.getElementsByTagName('font'):
                    if 'capella' in font.getAttribute('face'):
                        for content in text.getElementsByTagName('content'):
                            cont = latin1(content.firstChild.nodeValue)
                            if cont in dynamicSigns:
                                
                                # Das Ende einer Pedalklammer muss vor einem Dynamikzeichen stehen
                                if self.pedalUp <> '':
                                    self.writeBraille(self.pedalUp)
                                    self.pedalUp = ''

                                # das Ende eines wedge-Zeichens muss vor einem Dynamikzeichen stehen
                                while wedgeStorage.isfilled():
                                    self.writeBraille(wedgeStorage.getFirst())
                                    
                                self.writeBraille(dynamicSigns[cont])
                                self.printP3 = True
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!

    def handleWedge(self, n):
        # cresc. decresc.
        for drawObj in n.getElementsByTagName('drawObj'):
            noteRange = 0
            for basic in drawObj.getElementsByTagName('basic'):
                nr = basic.getAttribute('noteRange')
                if nr: noteRange = eval(nr)
                break
            for wedge in drawObj.getElementsByTagName('wedge'):

                # Das Ende einer Pedalklammer muss vor einem Dynamikzeichen stehen
                if self.pedalUp <> '':
                    self.writeBraille(self.pedalUp)
                    self.pedalUp = ''

                # das Ende eines wedge-Zeichens muss vor einem Dynamikzeichen stehen
                while wedgeStorage.isfilled():
                    self.writeBraille(wedgeStorage.getFirst())

                if wedge.getAttribute('decrescendo') == 'true':
                    self.writeBraille(decrescBegin)
                    wedgeStorage.add(noteRange, decrescEnd)
                else:
                    self.writeBraille(crescBegin)
                    wedgeStorage.add(noteRange, crescEnd)

                self.lastPitch = -100  # eine Oktavangabe erzwingen!


    def handleTrill(self,noteObject):
        trillFound = False
        
        shortVerticalLineFound = False
        for line in noteObject.getElementsByTagName('line'):
            x1 = line.getAttribute('x1')
            x2 = line.getAttribute('x2')
            y1 = line.getAttribute('y1')
            y2 = line.getAttribute('y2')
            if x1 and x2 and y1 and y2:
                if abs(float(x1) - float(x2)) < 0.5 and abs(float(y1) - float(y2)) < 3.0 :
                    shortVerticalLineFound = True

        for drawObj in noteObject.getElementsByTagName('drawObj'):
            noteRange = 0
            for basic in drawObj.getElementsByTagName('basic'):
                nr = basic.getAttribute('noteRange')
                if nr: noteRange = eval(nr)
                break

            for trill in drawObj.getElementsByTagName('trill'):
                tr = trill.getAttribute('tr')
                if noteRange > 0:
                    if tr == 'false':
                        self.writeBraille(trillSign)
                    else:
                        self.writeBraille(trSign)
                        self.lastPitch = -100  # eine Oktavangabe erzwingen!
                    self.writeBraille(continuationStartSign)
                    trillStorage.add(noteRange, continuationEndSign)
                elif tr == 'false':
                    if shortVerticalLineFound:
                        # langer Mordent (Trillerschlange mit kurzer senkrechter Linie)
                        self.writeBraille(longMordentSign)
                    else:
                        # langer Pralltriller
                        self.writeBraille(longTrillSign)
                else:
                    # normaler Triller eine Note
                    self.writeBraille(trSign)
                    self.lastPitch = -100  # eine Oktavangabe erzwingen!
                    
                trillFound = True

        if not trillFound:
            for text in noteObject.getElementsByTagName('text'):
                x = text.getAttribute('x')
                for font in text.getElementsByTagName('font'):
                    if 'capella' in font.getAttribute('face'):
                        for content in text.getElementsByTagName('content'):
                            cont = latin1(content.firstChild.nodeValue)
                            if cont == 'l':   # kurzer Pralltriller 
                                self.writeBraille(shortTrillSign)
                            elif cont == 't':   # Triller (tr)
                                self.writeBraille(trSign)
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                            elif cont == 'x':   # Mordent
                                self.writeBraille(shortMordentSign)
                            elif cont == 'w':   # Doppelschlag (liegendes S)
                                if x and float(x) > 1.0: # Doppelschlag zwischen den Noten
                                    self.writeBraille(dSchlagSign)
                                else:                   # Doppelschlag über/unter den Noten
                                    self.writeBraille(aboveSign + dSchlagSign)
                                    
                                if shortVerticalLineFound: # Umgekehrter Doppelschlag (senkrechte Linie durch liegendes S)
                                    self.writeBraille(reverseSign)
                                    


    def handleMusicalSymbolsBeforeNote(self, noteObject):
        if noteObject.tagName in ['chord', 'rest']:
            for text in noteObject.getElementsByTagName('text'):
                for font in text.getElementsByTagName('font'):
                    if 'capella' in font.getAttribute('face'):
                        for content in text.getElementsByTagName('content'):
                            cont = latin1(content.firstChild.nodeValue)
                            # Segno
                            if cont in segnoSign:
                                self.writeBraille(segnoSign[cont])
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                            # Coda
                            elif cont in codaSign:
                                self.writeBraille(codaSign[cont])
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                            # D.S.
                            elif cont in dsSign:
                                self.writeBraille(dsSign[cont])
                            # D.C.                                
                            elif cont in dcSign:
                                self.writeBraille(dcSign[cont])
                            # Pedalen ab
                            elif cont in pedalDownSign:
                                self.pedalDown = pedalDownSign[cont]
                            # Pedalen ab
                            if cont in pedalUpSign:
                                self.pedalUp = pedalUpSign[cont]
                            # Bogen auf- abstrich
                            elif cont in bowSign:
                                self.writeBraille(bowSign[cont])

        if self.pedalDown <> '':
            if self.pedalUp <> '':
                self.writeBraille(self.pedalUp)
                self.pedalUp = ''
            self.writeBraille(self.pedalDown)
            self.pedalDown = ''
            
                                
                            
    def handleMusicalSymbolsAfterNote(self, noteObject):
        if noteObject.tagName in ['chord', 'rest']:
            for text in noteObject.getElementsByTagName('text'):
                for font in text.getElementsByTagName('font'):
                    if 'capella' in font.getAttribute('face'):
                        for content in text.getElementsByTagName('content'):
                            cont = latin1(content.firstChild.nodeValue)
                            # Fermate
                            if cont in fermateSign:
                                self.writeBraille(fermateSign[cont])
        if self.pedalUp <> '':
            self.writeBraille(self.pedalUp)
            self.pedalUp = ''
        

    def handleArticulation(self, chord):
        articulation = []
        for art in chord.getElementsByTagName('articulation'):
            type = art.getAttribute('type')
            articulation.extend(string.split(type))

        for text in chord.getElementsByTagName('text'):
            font = text.gotoChild('font')
            content = text.gotoChild('content')
            if 'capella' in font.getAttribute('face'):
                if content.firstChild:
                    cont = latin1(content.firstChild.nodeValue)
                    if len(cont) == 1 and cont in articulationChars:
                        articulation.append(articulationChars[cont])

        for a in articulationsList:
            if a in articulation:
                if (a in ['staccato','tenuto']
                    and 'staccato' in articulation
                    and 'tenuto' in articulation):
                    if a == 'staccato':
                        self.writeBraille(mezzostaccato)
                else:
                    self.writeBraille(articulation2Braille[a])

    def handleVoltaSigns(self,n):
        if n.tagName in ['chord', 'rest']:
            for volta in n.getElementsByTagName('volta'):
                firstNumber = volta.getAttribute('firstNumber')
                lastNumber = volta.getAttribute('lastNumber')
                if firstNumber:
                    self.writeBraille(voltaSignBegin)
                    for ch in firstNumber:
                        self.writeBraille(digits2[int(ch)])
                    if lastNumber:
                        self.writeBraille(voltaSignContinue)
                        for ch in lastNumber:
                            self.writeBraille(digits2[int(ch)])
                else:
                    # Rest oder Voltazeichen ohne Nummer
                    self.writeBraille(voltaSignBegin)
                    self.writeBraille(digits2[1])  # 1.
                self.printP3 = True
                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                break # For-Schleife verlassen
        

    def handleLyric(self,n):
        for vers in n.getElementsByTagName('verse'):
            i = vers.getAttribute('i')
            hyphen = vers.getAttribute('hyphen')
            verseNumber = convertDigitsToBraille(latin1(vers.getAttribute('verseNumber')))
            extender = vers.getAttribute('extender')
            cont = ''
            if vers.firstChild:
                cont = latin1(vers.firstChild.nodeValue)
            if i:
                self.lyricDict[i] = self.lyricDict.setdefault(i, '') + cont
                if hyphen == 'true':
                    self.lyricDict[i] += hyphenSign
                elif extender == 'true':
                    self.lyricDict[i] += extenderSign
                else:
                    self.lyricDict[i] += ' '
                    
                if verseNumber:
                    self.lyricDict[i] = verseNumber + '$' + self.lyricDict[i]
                    self.lyricDict['lenVerseNumber'] = len(verseNumber) + 1


    def writeLyric(self):
        lenVersNumber = self.lyricDict.pop('lenVerseNumber', 0)
        prefix = lyricSign
        verses = self.lyricDict.keys()
        verses.sort()
        for i in verses:
            self.f.write('\n')
            if '$' in self.lyricDict[i]:
                s = string.replace(self.lyricDict[i], '$', ' ')
            else:
                s = ' ' * lenVersNumber + self.lyricDict[i]
            self.writeBraille(prefix + s)
            prefix = ' ' * len(lyricSign)


    def handleBreathSign(self, noteObject):
        if self.breathStorage <> '':
            self.writeBraille(self.breathStorage)
            self.breathStorage = ''
        for text in noteObject.getElementsByTagName('text'):
            x = text.getAttribute('x')
            for content in text.getElementsByTagName('content'):
                cont = latin1(content.firstChild.nodeValue)
                if cont in [',' , "'"]:
                    if float(x) < 0.5:
                        self.writeBraille(breathSign) # atemzeichen links von Note
                    else:
                        self.breathStorage = breathSign # atemzeichen rechts von Note

    def handleText(self, noteObject):
        self.printP3 = False
        textFound = False
        setWordSign = False
        drawObjList = []
        for drawObjects in noteObject.getElementsByTagName('drawObjects'):
            for child in drawObjects.getChildNodes():
                if child.getElementsByTagName('transposable'):
                    pass
                else:
                    drawObjList.append(child)
        
        for drawObj in drawObjList:
            for text in drawObj.getElementsByTagName('text'):
                x = text.getAttribute('x')
                y = text.getAttribute('y')
                for content in text.getElementsByTagName('content'):
                    cont = latin1(content.firstChild.nodeValue)
                    cont = convertDigitsToBraille(cont)

                    # römische Bezifferung I, II, III ausfiltern ind durch >i[i[i]] ersetzen 
                    iCount = 0
                    for i in range(len(cont)):
                        if cont[i] == 'I':
                            iCount += 1
                        elif iCount and cont[i] in ['.', ' ']:
                            break
                        else:
                            iCount = 0
                            break
                    if iCount:
                        cont = '>' + 'i' * iCount + cont[iCount:]

                    conts = string.split(cont)
                    for font in text.getElementsByTagName('font'):
                        if 'capella' in font.getAttribute('face'):
                            # capella-Text wird ignoriert
                            continue
                        elif not iCount and len(cont) <= 2:
                            # kurzer Text wird ausgefiltert
                            continue
                        elif not (-6.0 < float(y) < 8.0):
                            # Text darf nicht zu weit von Notenlinie entfernt sein
                            continue
                        else:
                            if len(conts)  <= 2:  # Text aus max. 2 Blöcken
                                for c in conts:
                                    self.writeBraille('@%s' % (c))
                                setWordSign = False
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                                textFound = True
                            else:  # langer Text 
                                if not textFound and self.lastBase <> '0':
                                    # Wenn dies der erste Text ist und der Text nicht am Taktanfang steht
                                    self.writeBraille(barSeparation)  
                                self.writeBraille('@%s' % (cont))
                                setWordSign = True
                                self.lastPitch = -100  # eine Oktavangabe erzwingen!
                                textFound = True

        if setWordSign:
            self.writeBraille('@ ')
        elif textFound and cont[len(cont)-1:] <> '.':
            self.printP3 = True
        

    def startNoteObj(self, n):
        if self.barSeparation:
            self.writeBraille(barSeparation)
            self.barSeparation = False

        self.handleBarline(n)            

        if n.tagName in ('chord', 'rest'):

            wedgeStorage.next()
            trillStorage.next()

            if self.firstNote:
                self.writeBraille(' ')
                self.lastBase = '0'
                self.firstNote = False

            #Volta Signs
            self.handleVoltaSigns(n)

            # Tuplets
            if self.tupletTime <= Rational('0'):
                self.triplet = False
                for tuplet in n.getElementsByTagName('tuplet'):
                    if tuplet.hasAttribute('count'):
                        count = tuplet.getAttribute('count')
                        if count == '3':
                            self.writeBraille(';')
                        else:
                            self.writeBraille('_')
                            for ch in count:
                                self.writeBraille(digits2[int(ch)])
                            self.writeBraille('.')
                        self.triplet = True       # damit wird der Wert des Tuplets berechnet
                            
            # Bindebogen
            for drawObj in n.getElementsByTagName('drawObj'):
                if len(drawObj.getElementsByTagName('slur')) > 0:
                    basic = drawObj.getElementsByTagName('basic')
                    if len(basic) > 0:
                        noteRange = eval(basic[0].getAttribute('noteRange'))
                        isPhrase = len(drawObj.getElementsByTagName('form')) > 0   # punktierter Bindebogen
                        if noteRange in [1,2,3]:
                            # Das Zeichen C wird für einen Bindebogen über höchstens vier Noten benutzt.
                            # Es steht nach jeder Note mit Ausnahme der letzten.
                            if isPhrase:
                                for i in range(0, noteRange):
                                    slurStorage.add(i + 1, slurPhrase)
                            else:                                
                                for i in range(0, noteRange):
                                    slurStorage.add(i + 1, slurShort)
                        elif noteRange > 3:
                            # Das Zeichen <B steht vor der ersten Note der Phrase und
                            # das Zeichen >; nach der letzten Note der Phrase
                            self.writeBraille(slurBegin)
                            slurStorage.add(noteRange + 1, slurEnd)
                    else:
                        # Bindebogen hängt nur an einer Note
                        # ToDo
                        pass

            # diverse Musiksymbole vor der Note
            self.handleMusicalSymbolsBeforeNote(n)

            # Atemzeichen
            self.handleBreathSign(n)

            # Text
            self.handleText(n)

            # Triller / Mordent
            self.handleTrill(n)

            # Dynamikzeichen 
            self.handleDynamics(n)

            # cresc. decresc.
            self.handleWedge(n)

            # Artikulationszeichen
            self.handleArticulation(n)

            # Liedtext
            self.handleLyric(n)
            

    def calculateChords(self,n):
        self.intervalSigns = ''
        self.octaveSign = ''
        if n.tagName <> 'chord':
            return            
        
        if self.clefSign[0] in ['G','C']:
            # höchste Note zuerst bei G und C Schlüssel
            self.pitches.reverse()
            alterOctave = -1
        else:
            # tiefste Note zuerst bei F Schlüssel
            self.pitches.sort()
            alterOctave = +1

        pitch, alteration = self.pitches[0]
        self.relPitch = pitch % 7
        octave = pitch // 7
        self.pitch = pitch
        self.alteration = alteration

        interval = abs(pitch - self.lastPitch)

        # Oktave schreiben (Regel siehe oben!)
        if interval > 4 or interval > 2 and pitch//7 != self.lastPitch//7:
            self.octaveSign = octaves[octave-1]
        else:
            self.octaveSign = ''

        self.lastPitch = pitch

        # Intervalle bestimmen
        I = intervals(self.pitches)
        self.intervalSigns = ''
        if I <> 0:
            # Intervalle vorhanden
            i = 0
            for (p,a) in I:
                if abs(self.pitches[i+1][0] - self.pitches[i][0]) > 8:
                    # Intervall ist grösser als Oktave dann Oktavzeichen
                    self.intervalSigns += octaves[octave-1 + alterOctave]
                self.intervalSigns += intervalSigns[p]
        

    def setAlterSign(self, alterSign, sign):
        # alterSign - von Note
        # sign - in Tonart enthalten
        if self.alterationSigns.get(self.pitch) == alterSign:
            pass
        elif alterSign <> sign:
            self.writeBraille(alterSign)
            self.alterationSigns[self.pitch] = alterSign
        elif not self.alterationSigns.get(self.pitch):
            pass
        elif self.alterationSigns.get(self.pitch) <> alterSign:
            self.writeBraille(alterSign)
            self.alterationSigns[self.pitch] = alterSign

    def finishNoteObj(self, n):
        if n.tagName in ('chord', 'rest'):

            # Haltebogen auswerten
            if self.hasTie:
                if self.nHeads > 1 and self.allTie:
                    slurStorage.add(1, allTieSign)
                else:
                    slurStorage.add(1, tieSign)
                    
            slurStorage.next()
            self.barline = False
            d = n.getElementsByTagName('duration')[0]
            base = str(d.getAttribute('base'))
            #d = Rational(base)
            t = base.split('/')
            p = t[0].strip()
            if len(t) > 1:
                q = t[1].strip()
            else:
                q = 1
            d = Rational(int(p),int(q))

            # Notenwert und Intervalle berechnen
            self.calculateChords(n)
            
            # Notenwert                
            if n.tagName == 'rest':
                self.relPitch = 7

            # kleines Wertscheidungszeichen am Zeilenbeginn, wenn Takt / 1ste Note == 16
            #    z.B. 4/4 und 16tel
            if self.firstNote:
                if self.meter / Rational(base) == Rational(16):
                    self.writeBraille(valueSepSmaller)
                
            # zwischen Noten mit gleichem Namen, aber unterschiedlichem Wert wird ein Wertscheidungszeichen gesetzt
            baseQ = Rational(self.lastBase) / Rational(base)
            if  baseQ == Rational(16) and base <> '1/256':
                self.writeBraille(valueSepSmaller)
            elif baseQ == Rational('1/16'):
                self.writeBraille(valueSepGreater)
            elif base == '1/256':
                if self.lastBase <> '1/256':
                    self.writeBraille(prefix256)
                
            # Vorzeichen
            if n.tagName == 'chord':
                pitch = 'CDEFGAB'[self.relPitch]
                alterSign = ['22','2','1','3','33'][self.alteration + 2]
                if self.fifths > 0:
                    # messageBox('self.fifth',str(self.pitch) + ' ' + str(self.fifths))
                    alterPitch = pitch in 'FCGDAEB'[0:self.fifths]
                    if alterPitch:
                        self.setAlterSign(alterSign,'3')
                    else:
                        self.setAlterSign(alterSign,'1')
                        
                elif self.fifths < 0:
                    alterPitch = pitch in 'BEADGCF'[0:-self.fifths]
                    if alterPitch:
                        self.setAlterSign(alterSign,'2')
                    else:
                        self.setAlterSign(alterSign,'1')
                else:  # C-Dur
                    self.setAlterSign(alterSign,'1')

            # Oktavzeichen
            if self.octaveSign <> '':
                self.writeBraille(self.octaveSign)
                self.octaveSign = ''

            # Noten / Pausen
            if   base == '1/1':
                self.writeBraille(wholeNotes  [self.relPitch])
            elif base == '1/16':
                self.writeBraille(wholeNotes  [self.relPitch])
            elif base == '1/2':
                self.writeBraille(halfNotes   [self.relPitch])
            elif base == '1/32':
                self.writeBraille(halfNotes   [self.relPitch])
            elif base == '1/4':
                self.writeBraille(quarterNotes[self.relPitch])
            elif base == '1/64':
                self.writeBraille(quarterNotes[self.relPitch])
            elif base == '1/8':
                self.writeBraille(eighthNotes [self.relPitch])
            elif base =='1/128':
                self.writeBraille(eighthNotes [self.relPitch])
            elif base == '1/256':
                self.writeBraille(wholeNotes  [self.relPitch])

            # Kasten-, Mehrtaktpausen
            elif  n.tagName == 'rest' and d.q == 1:
                i = d.p
                if i in [1,2,3]:
                    self.writeBraille(i * wholeNotes  [self.relPitch])
                elif i > 3:
                    b = convertDigitsToBraille(str(i))
                    self.writeBraille( b + wholeNotes  [self.relPitch])

            # Kastennoten
            elif  n.tagName == 'chord' and d.q == 1:
                i = d.p
                self.writeBraille(wholeNotes  [self.relPitch])
                i -= 1
                while i > 0:
                    self.writeBraille(boxSign)
                    self.writeBraille(wholeNotes  [self.relPitch])
                    i -= 1
            else:
                self.writeBraille(undefined)

            self.lastBase = base

            # Notendauer berechnen
            if n.getElementsByTagName('duration').length == 0:
                duration = Rational('0')
            elif  n.tagName == 'rest' and d.q == 1:   # Ganz- und Mehrtaktpausen 
                self.time = self.meter
                duration = Rational('0')
            else:
                dur = n.getElementsByTagName('duration')[0]
                if dur.getAttribute('noDuration') == 'true':
                    duration = Rational('0')
                else:
                    duration = Rational(str(dur.getAttribute('base')))
                    count = '1'
                    for tuplet in dur.getElementsByTagName('tuplet'):
                        if tuplet.hasAttribute('count'):
                            count = tuplet.getAttribute('count')
                        else:
                            count = '1'
                        if tuplet.hasAttribute('tripartite') and tuplet.getAttribute('tripartite') == 'true':
                            #                   0     1     2     3     4     5     6     7     8     9     10     11     12      13
                            factor = Rational(['1/1','1/1','3/4','2/3','3/4','3/5','3/6','6/7','6/8','6/9','6/10','6/11','12/12','12/13','12/14','12/15'][eval(count)])
                        else:
                            factor = Rational(['1/1','1/1','2/2','2/3','4/4','4/5','4/6','4/7','8/8','8/9','8/10','8/11','8/12', '8/13', '8/14', '8/15' ][eval(count)])
                        if tuplet.hasAttribute('prolong') and tuplet.getAttribute('prolong') == 'true':
                            factor = factor * 2
                        duration = duration * factor
                        
                    if dur.hasAttribute('dots'):
                        if dur.getAttribute('dots') == '1':
                            duration = duration * Rational('3/2')
                            self.writeBraille('.')
                        else:
                            duration = duration * Rational('7/4')
                            self.writeBraille('..')

                    # Wenn triplet gesetzt ist, wird die Tuplet Dauer anhand der ersten Note berechnet
                    if self.triplet:
                        self.triplet = False
                        self.tupletTime = Rational(str(count)) * duration

            if self.tupletTime > Rational('0'):
                self.tupletTime -= duration

            self.time += duration

            #IntervalLzeichen
            self.writeBraille(self.intervalSigns)                

            # diverse Musiksymbole nach der Note
            self.handleMusicalSymbolsAfterNote(n)

            # Witergeltungslinie Ende
            while trillStorage.isfilled():
                self.writeBraille(trillStorage.getFirst())

            # Bindebogen
            while slurStorage.isfilled():
                self.writeBraille(slurStorage.getFirst())

            # Crescendo, Decrescendo
            while wedgeStorage.isfilled():
                self.writeBraille(wedgeStorage.getFirst())
                self.lastPitch = -100  # eine Oktavangabe erzwingen!


    def handleHead(self, head):
        p = head.getAttribute('pitch')
        pitch = 'CDEFGAB'.find(p[0]) + 7 * int(p[1])
        # Vorzeichen -2 ... +2, bb ... ##
        alteration = 0
        for alter in  head.getElementsByTagName('alter'):
            if alter.getAttribute('step'):
                alteration = int(alter.getAttribute('step'))
        self.pitches += [(pitch, alteration)]

        # Haltebogen
        hasTie = False
        for tie in head.getElementsByTagName('tie'):
            if tie.getAttribute('begin') == 'true':
                hasTie = True
        if hasTie:
            self.hasTie = True
        else:
            self.allTie = False
                
        self.nHeads += 1

#-----------------------------------------------------------------------

def writeBrailleRel():
    """ schreibt die aktuelle Partitur unter gleichem Namen mit Erweiterung '.braille'
    """
    if activeScore():
        if not ':' in activeScore().pathName():
            messageBox('Braille-Export', 'Bitte Partitur zunächst speichern')
            return
        tmpFile = tempfile.mktemp('.capx')
        
        h, t = os.path.split(activeScore().pathName())

        dirHandling = opt.setdefault('BrailleDirectoryHandling','0')
        if dirHandling == '0':
            ownDir = h
        elif dirHandling == '1':
            ownDir = os.path.join(h, 'Braille')
            opt['BrailleDirectory'] = ownDir
        elif dirHandling == '2':
            ownDir = opt.get('BrailleDirectory')
            if ownDir :
                pass
            else:
                ownDir = os.path.join(h, 'Braille')
                opt['BrailleDirectory'] = ownDir

        if not os.path.isdir(ownDir):
            os.makedirs(ownDir)

        options.set(opt)

        outFile = os.path.join(ownDir, os.path.splitext(t)[0] + '.braille')

        activeScore().write(tmpFile)
        BrailleExport(tmpFile, outFile)
        os.remove(tmpFile)

def writeBrailleAbs():
    """ schreibt die aktuelle Partitur in eine feste Datei
    """
    if activeScore():
        tmpFile = tempfile.mktemp('.capx')
        outFile = r'c:\test.braille' # nach Wunsch anpassen !!!
        activeScore().write(tmpFile)
        BrailleExport(tmpFile, outFile)
        os.remove(tmpFile)

writeBrailleRel()

#---------------- weiter runter gehts wirklich nicht ----------------