# -*- coding: ISO-8859-1 -*-
""" capellaScript -- Bernd Jungmann
>>> Ausgewählte Stimmen Vorspielen

Mit Start beim Curser wird die Partitur vorgespielt. Zunächst erscheint
eine Dialogbox, in der für jede in der Partitur vorkommende Stimme ein
Auswahl-Kästchen angeboten wird, mit dem das Vorspiel dieser Stimme
deaktiviert werden kann. Dabei wird für die Bezeichnung der Auswahl-
Kästchen die Stimmen-"Beschreibung" aus dem Mustersystem verwendet.
Drückt man OK, so startet das Vorspiel beim Cursor.

Dieses Skript ist unvollkommen in folgenden Punkten:

Es gibt keine Möglichkeit, das Vorspiel zu einem beliebigen Zeitpunkt
zu unterbrechen. Deshalb wird am Anfang eines Blocks von wahlweise bis
zu 5 Notenzeilen eine Dialogbox gezeigt, die die Wahl zwischen weiterspielen
und abbrechen ermöglicht. Diese Dialogbox erscheint auch nach jedem
Sprungbefehl z.B. bei Wiederholungen. Die Auswahl der Anzahl der ohne
Unterbrechung abzuspielenden Notenzeilen erfolgt auf der Eingangs-Dialogbox.

Beim Vorspiel wird die Instrumentenzuordnung im Mustersystem berücksichtigt.

Haltebögen, Wiederholungen und einfache Voltenklammern werden ebenfalls
berücksichtigt.

Haltebögen werden fehlertolerant interpretiert: Falls der rechte oder
linke Partner eines Haltebogens fehlt, wird die Note als Einzelnote
gespielt

Die Berücksichtigung von Ablaufvorschriften mit Segno und Kopf ist 
notwendigerweise noch etwas experimentell, da hierfür klare Anweisungen
im Datenkonzept von Capella bisher fehlen. Segno und Kopf werden in der
vorliegenden Implementation wie folgt berücksichtigt:
Segno und Kopf werden gefunden, wenn sie als Einfachtext-Musiksymbol
ausgeführt sind mit genau einem Zeichen.
Wird irgendwann ein Einfach-Text gefunden, der am Anfang die Zeichenfolge d.S.
oder D.S. enthält, so spring das Vorspiel zum letzten gefundenen Segno.
Von dort aus arbeitet es Wiederholungen und Voltenklammern normal ab, versucht
jedoch beim Erreichen eines Codakopfes einen Sprung zur Coda auszuführen.
Der Anfang der Coda wird zur Zeit (vorläufig!) an der Stelle vermutet,
wo der Sprungbefehl zum Segno gefunden wurde.
Findet sich ein Kopf innerhalb eines zu wiederholenden Bereiches, so wird
der Sprung bereits beim ersten Durchgang ausgeführt.
Findet sich ein Segno innerhalb eines zu wiederholenden Bereiches, so 


Version 1: Bernd Jungmann 7.2.04
Version 2: Bernd Jungmann 23.2.04
<<<
"""

import time
import threading
import tempfile
import zipfile, tempfile, os, xml.dom.minidom

def getMoment(score, (sysind, staffind, voiceind, objind)):
    sys = score.system(sysind)
    staff = sys.staff(staffind)
    voice = staff.voice(voiceind)
    no = voice.nNoteObjs()
    if objind < no:
	obj = voice.noteObj(objind)
        while (not obj.isChord()) and (not obj.isRest()) and objind + 1 < no:
            objind += 1
            obj = voice.noteObj(objind)
        if obj.isChord():
            return obj.time()
        elif obj.isRest():
            return obj.time()
    return 10000.0   # sehr große Gleitkommazahl



# In allen Systemen nachschauen, ob mehrere Stimmen vorhanden sind
def getmaxvoices(as, maxvoicesinstave):
    vl = as.voiceList()
    for vname in vl:
        maxvoicesinstave.append(1)
    for sys in as.systems():
        for staff in sys.staves():
            sind = staff.index()
            nv = staff.nVoices()
            if maxvoicesinstave[sind] < nv:
                maxvoicesinstave[sind] = nv

# Hilfsfunktion
def getNthChildElement(el, childTag, Nth):
    count = 0
    for n in el.childNodes:
        if n.nodeType == n.ELEMENT_NODE and n.tagName == childTag:
            if count == Nth:
                return n
            count += 1
    return None
def getContent(el):
    for n in el.childNodes:
        if n.nodeType == n.TEXT_NODE:
            return n.nodeValue
    return None
# Jede Stimme erkennt das Sprungziel, es soll aber
# nur ein "repbegin"-Sprungziel in sprungziele geben
def schonVorhanden(sprungziele, testtyp):
    for typ, pos in sprungziele:
        if typ == testtyp:
            return 1
    return 0

# playfromhere parameter:
#
# start             Startposition (wie sie von curSelection() geliefert wird)
# pitchespending    Array von Directories, in denen die über playfromhere hinaus
#                   anzuhaltenden Töne notiert werden
# score             Partitur wie von activeScore() geliefert
# repcount          Wiederholungszähler (1..n)
# sprungziele       Liste von Tupeln aus Sprung-Zieltyp und Cursorposition
#                   Sprung-Zieltypen können sein "goonstart", "repbegin", "coda", "coda2",
#                   "capo", "segno", "segno2", "segno3", "volta2", "volta3", "volta4"
# systemsTxt
# staffLayoutsTxt
# descrind
# checktext
# killev
#
# Rückgabewert      Sprungbefehl gefunden ("goon", "repend", "jmpvolta1", "jmpvolta2", "jmpvolta3",
#                   "jmpcoda", "jmpcoda2", "d.C.", "d.S.", "d.S.2", "d.S.3")
#
actioncode = [
                    # Die Reihenfolge dieser Liste hat (bei gleichem moment) Auswirkugen auf plan.sort()!
                    # Sprungziele
    "goonstart",    # 0
    "repbegin",     # 1
    "coda",         # 2
    "coda2",        # 3
    "capo",         # 4
    "segno",        # 5
    "segno2",       # 6
    "segno3",       # 7
    "volta2",       # 8
    "volta3",       # 9
    "volta4",       # 10
                    # Sprungbefehle
    "goon",         # 11
    "repend",       # 12
    "jmpvolta1",    # 13
    "jmpvolta2",    # 14
    "jmpvolta3",    # 15
    "jmpcoda",      # 16
    "jmpcoda2",     # 17
    "d.C.",         # 18
    "d.S.",         # 19
    "d.S.2",        # 20
    "d.S.3",        # 21
                    # Spielbefehle
    "",             # 22 (Ton aus)
    "",             # 23 (Ton an)
    "",             # 24 (nichts tun)
    ]
                    # Sprungziele
Cgoonstart = 0
Crepbegin = 1
Ccoda = 2
Ccoda2 = 3
Ccapo = 4
Csegno = 5
Csegno2 = 6
Csegno3 = 7
Cvolta2 = 8
Cvolta3 = 9
Cvolta4 = 10
CmaxSprungziel = 10
                    # Sprungbefehle
Cgoon = 11
Crepend = 12
Cjmpvolta1 = 13
Cjmpvolta2 = 14
Cjmpvolta3 = 15
Cjmpcoda = 16
Cjmpcoda2 = 17
CdC = 18
CdS = 19
CdS2 = 20
CdS3 = 21
CmaxSprungbefehl = 21
                    # Spielbefehle
Coff = 22           # 22 (Ton aus)
Con = 23            # 23 (Ton an)
Cpass = 24          # 24 (nichts tun)

Epsilon = 0.0000001 # kleine Gleitkommazahl, kleiner als die Zeit-Lücke zwischen 2
                    # aufeinanderfolgenden Tönen

def hasVolta1(noteobjTxt):
    volta = noteobjTxt.getElementsByTagName('volta')
    if volta != []:
        if '1' == volta[0].getAttribute('firstNumber'):
            return 1
    return 0
    
def hasJmpText(noteobjTxt, actionIndex, zr):
    texts = noteobjTxt.getElementsByTagName('text')
    if texts != []:
        for text in texts:
            font = getNthChildElement(text, 'font', 0)
            contentElement = getNthChildElement(text, 'content', 0)
            content = getContent(contentElement)
#            messageBox("Text gefunden", "face %s, content %s" % (font.getAttribute('face'), str(content)))
            if 'capella3' == font.getAttribute('face'):
                if actionIndex == Csegno:
                    if content == 'y':
                        return 1
                if actionIndex == Cjmpcoda:
                    if content == 'n':
                        return 1
            else:
                if actionIndex == CdS:
                    if "d.S." == content[0:4] or "D.S." == content[0:4]:
                        return 1
                if actionIndex == CdC:
                    if "d.C." == content[0:4] or "D.C." == content[0:4]:
                        return 1
    richtext = noteobjTxt.getElementsByTagName('richText')
    if len(richtext) > 0:
        return 0    # hier erstmal nichts mehr machen.
        file = richtext[0].getAttribute('file')
        for name in zr.namelist():
            t = zr.read(name)
            if name == file:
                messageBox("RichText Datei gefunden", file)
                # ich kann aber bisher in der Rich-Text-Datei nichts lesen.
                return 0
        messageBox("RichText Datei nicht gefunden", file)
    return 0
    


def playfromhere(start, pitchespending, score, repcount, watchcoda,
        sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod):
    startmoment = getMoment(score, start)
    sysnr = 0
    firstsys = 1
    jmpfound = ''
    pending = {}
    skipsys = 0
    for sys in as.systems():
        if firstsys and start[0] > sysnr:
            sysnr += 1
            skipsys += 1
            continue
        sysTxt = getNthChildElement(systemsTxt, 'system', sysnr)
        sysnr += 1
        Tempo = sys.get('tempo')
#        TempoTxt = sysTxt.getAttribute('tempo')# ist nur vorhanden, wenn es von 120 oder vom bisherigen Tempo abweicht!
        if stopperiod > 0 and (sysnr - 1 - start[0] - skipsys) % stopperiod == 0:
            if 1 != messageBox("Ausgewählte Stimmen vorspielen", "jetzt müßten %d Systeme ab %d gespielt werden im Tempo %.3f." % (stopperiod, sysnr, Tempo), 0, 1):
                break
# plan soll eine Liste der Abspielereignisse oder playevents werden. Jedes Abspielereignis ist ein
# Tupel (moment, on, nChannel, nNote, nVolume) dabei bedeutet
#
# moment    der Zeitpunkt des Ereignisses von Beginn des Systems an gerechnet,
# on        ist die Aktions-Variable:
#           0 heißt ausschalten,
#           1 heißt einschalten,
#           2 heißt nichts tun (für Zeilenwechsel),
# nChannel  ist der MIDI-Kanal,
# nNote     ist die Tonhöhe
# nVolume   die Lautstärke.
        plan = []
        defaultMeter = 1.0
        StavesTxt = getNthChildElement(sysTxt, 'staves', 0)
        stavesTxt = StavesTxt.getElementsByTagName('staff')
        voiceind = 0    # Dies ist der Index für decrind, läuft über alle Zeilen eines Systems!
        stopmoment = 10000.0   # der Zeitpunkt, an dem ein Sprungbefehl gefunden wurde
        while voiceind < len(checktext):
            playthisvoice = 0
            if checktext[voiceind].value():
                playthisvoice = 1
            descr, i = descrind[voiceind]
            istaff = sys.staffIndexFromDescr(descr)
            if istaff >= 0 and istaff < len(staffLayoutsTxt):
                assert istaff < len(stavesTxt) 
                staffLayoutTxt = staffLayoutsTxt[istaff]
                soundTxt = getNthChildElement(staffLayoutTxt, 'sound', 0)
                Instr = soundTxt.getAttribute('instr')
                Volume = soundTxt.getAttribute('volume')
                staff = sys.staff(istaff)
                staffTxt = stavesTxt[istaff]
                MidiOut.setMainVolume(istaff % 16, int(Volume))
                MidiOut.programChange(istaff % 16, int(Instr))
                nvoices = staff.nVoices()
                VoicesTxt = getNthChildElement(staffTxt, 'voices', 0)
                voicesTxt = staffTxt.getElementsByTagName('voice')
                nvoicesTxt = len(voicesTxt)
                assert nvoices == nvoicesTxt
                if i < nvoices:
                    MidiChan = istaff % 16
                    MidiVolume = int(Volume)
                    voice = staff.voice(i)
                    voiceTxt = voicesTxt[i]
                    NoteObjectsTxt = getNthChildElement(voiceTxt, 'noteObjects', 0)
                    indNoteobj = -1
                    indNoteobjTxt = -1
                    lastmoment = 0.0    # der letzte von einer Note oder Pause abgedeckte Zeitpunkt
                                        # wird gebraucht, falls beim Zeilenübergang kein Ton anfängt oder aufhört
                                        # und für die Sprungbefehle
                        
                    for noteobj in voice.noteObjs():
                        while NoteObjectsTxt.childNodes[indNoteobjTxt].nodeType != NoteObjectsTxt.childNodes[indNoteobjTxt].ELEMENT_NODE:
                            indNoteobjTxt += 1
                        noteobjTxt = NoteObjectsTxt.childNodes[indNoteobjTxt]
                        indNoteobjTxt += 1
                        indNoteobj += 1
#                        if watchcoda:
#                            messageBox("Watch Coda","Obj %d: %s" % (indNoteobj,noteobjTxt.tagName))
                        if noteobj.isChord():
                            if noteobjTxt.tagName != "chord":
                                messageBox("MeinScript falscher tagName", noteobjTxt.tagName)
                            moment = noteobj.time()
                            duration = noteobj.duration()
                            if firstsys:
                                if moment < startmoment - 0.00001: # muß mit Ungenauigkeiten rechnen!
                                    continue
                                else:
                                    moment -= startmoment
                            # hängt ein Sprungziel oder Sprungbefehl dran?
                            if repcount == 2:
                                if hasVolta1(noteobjTxt):
                                    plan.append((moment, Cjmpvolta1, 0,0,0))
                            if repcount == 1:
                                if not watchcoda and hasJmpText(noteobjTxt, Csegno, zr):
                                    plan.append((moment, Csegno, istaff, i, indNoteobj))
                            if watchcoda == 1:
                                if hasJmpText(noteobjTxt, Cjmpcoda, zr):
                                    plan.append((moment, Cjmpcoda, istaff, i, indNoteobj))
                            if lastmoment < moment + duration:
                                lastmoment = moment + duration
                            if playthisvoice:
                                for head in noteobj.heads():
                                    tieTxts = noteobjTxt.getElementsByTagName('tie')
                                    tiebegin = 0
                                    tieend = 0
                                    if(tieTxts != []):
                                        tieTxt = tieTxts[0]
                                        if 'true' == tieTxt.getAttribute('begin'):
                                            tiebegin = 1
                                        if 'true' == tieTxt.getAttribute('end'):
                                            tieend = 1
                                    ii = head.index()
                                    pitch = head.chromaticPitch()
                                    if tieend == 1:
                                        if pitch in pitchespending[voiceind]:
                                            del pitchespending[voiceind][pitch]
                                        else:
                                            # Es gibt einen Fehler in der Partitur: Der erste Ton, der zu
                                            # dem hier endenden Haltebogen gehört, war nicht auf gleicher
                                            # Tonhöhe, wenn er überhaupt existiert. Schalte immerhin diesen
                                            # zweiten Ton an!
                                            plan.append((moment, Con, MidiChan, pitch, MidiVolume))
                                    else:
                                        plan.append((moment, Con, MidiChan, pitch, MidiVolume))
                                    if tiebegin == 1:
                                        pitchespending[voiceind][pitch] = (sysnr-1,indNoteobj)
                                    else:
                                        plan.append((moment + duration * 0.99, Coff, MidiChan, pitch, MidiVolume))
                                # Nach jedem Akkord aufräumen: alle Töne ausschalten, die zwar einen Haltebogenanfang
                                # hatten, aber keinen Partner.
                                ppcopy = pitchespending[voiceind].items()
                                for key,value in ppcopy:
                                    if value < (sysnr-1,indNoteobj):
                                        plan.append((moment, Coff, MidiChan, key, MidiVolume))
                                        del pitchespending[voiceind][key]
                            # Sprungbefehle der Art d.S. al fine/Kopf
                            if not watchcoda and hasJmpText(noteobjTxt, CdS, zr):
                                plan.append((lastmoment, CdS, istaff, i, indNoteobj))
                                if playthisvoice:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj+1))
                            if not watchcoda and hasJmpText(noteobjTxt, CdC, zr):
                                plan.append((lastmoment, CdC, istaff, i, indNoteobj))
                                if playthisvoice:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj+1))
                        elif noteobj.isRest():
                            # Pausen sind für den Abspielplan nur von Bedeutung, falls sie 
                            # die letzten Objekte vor einem Zeilenumbruch sind.
                            if noteobjTxt.tagName != "rest":
                                messageBox("MeinScript falscher tagName", noteobjTxt.tagName)
                            moment = noteobj.time()
                            duration = noteobj.duration()
                            if firstsys:
                                if moment < startmoment - 0.00001: # muß mit Ungenauigkeiten rechnen!
                                    continue
                                else:
                                    moment -= startmoment
                            # hängt ein Sprungziel oder Sprungbefehl dran?
                            if repcount == 2:
                                if hasVolta1(noteobjTxt):
                                    plan.append((moment, Cjmpvolta1, 0,0,0))
                            if repcount == 1:
                                if not watchcoda and hasJmpText(noteobjTxt, Csegno, zr):
                                    plan.append((moment, Csegno, istaff, i, indNoteobj))
                            if watchcoda == 1:
                                if hasJmpText(noteobjTxt, Cjmpcoda, zr):
                                    plan.append((moment, Cjmpcoda, istaff, i, indNoteobj))
                            if lastmoment < moment + duration:
                                lastmoment = moment + duration
                            # Sprungbefehle der Art d.S. al fine/Kopf
                            if not watchcoda and hasJmpText(noteobjTxt, CdS, zr):
                                plan.append((lastmoment, CdS, istaff, i, indNoteobj))
                                if playthisvoice:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj+1))
                            if not watchcoda and hasJmpText(noteobjTxt, CdC, zr):
                                plan.append((lastmoment, CdC, istaff, i, indNoteobj))
                                if playthisvoice:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj+1))
                        elif noteobj.isBarline():
                            if firstsys and startmoment > 0 and lastmoment == 0:
                                continue
                            assert noteobjTxt.tagName == "barline"
                            Type = noteobjTxt.getAttribute('type')
                            if Type == 'repEnd':
                                if repcount <= 1:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj))
                                    plan.append((lastmoment, Crepend, 0,0,0))
                                else:
                                    plan.append((lastmoment, Cgoon, 0,0,0))
                            elif Type == 'repBegin':
                                if repcount <= 1:
                                    plan.append((lastmoment, Crepbegin, istaff, i, indNoteobj))
                            elif Type == 'repEndBegin':
                                if repcount <= 1:
                                    plan.append((lastmoment, Crepend, 0,0,0))
                                else:
                                    plan.append((lastmoment, Cgoonstart, istaff, i, indNoteobj))
                            
                    # Todo: Am Ende jeder Zeile die noch klingenden Töne notieren
                    plan.append((lastmoment, Cpass, 0,0,0))
            voiceind += 1
        firstsys = 0


        plan.sort()
        lastMoment = 0.0
#        messageBox("MeinScriptPlan", str(plan))
        for playevent in plan:
            if killev.isSet():
                break
            moment, action, MidiChan, pitch, MidiVolume = playevent
            if moment < stopmoment:
                towait = moment - lastMoment
                # 240 = 4 Viertel * 60 Sekunden
                time.sleep(towait * 240 / Tempo )
                if action == Con:
                    MidiOut.noteOn(MidiChan, pitch, MidiVolume)
                    pending[pitch] = MidiChan
                elif action == Coff:
                    MidiOut.noteOff(MidiChan, pitch)
                    if pitch in pending:
                        del pending[pitch]
                elif action == Cpass:   # Zeilenumbruch
                    pass
                # Sprungziele: MidiChan, pitch, MidiVolume sind in Wirklichkeit istaff, ivoice, indNoteobj
                elif action <= CmaxSprungziel:
                    if not schonVorhanden(sprungziele, actioncode[action]):
                        sprungziele.append((actioncode[action],(sysnr - 1, MidiChan, pitch, MidiVolume)))
                # Sprungbefehle: MidiChan, pitch, MidiVolume sind irrelevant
                elif action <= CmaxSprungbefehl:
                    jmpfound = actioncode[action]
                    break
                lastMoment = moment
#        messageBox("MeinScript", "Zeilenplan fertig abgespielt")
        if jmpfound != '':
            # Ein Sprung muß ausgeführt werden. Das kann dazu führen,
            # daß ein Ton noch klingt, der nicht in pitchespending
            # enthalten ist (weil sein Ausmachen kurz nach der Sprungstelle
            # richtig in den Plan geschrieben wurde)
            pcopy = pending.items()
            for key, value in pcopy:
                # Wenn's in pitchespending gefunden wird, ist es ok.
                # Sonst muß der Ton jetzt ausgemacht werden.
                vi = 0
                ok = 0
                while vi < len(pitchespending):
                    if key in pitchespending[vi]:
                        ok = 1
                    vi += 1
                if not ok:
                    MidiOut.noteOff(value, key)
            break
#    messageBox("pitchespending vor Ende playfromhere",str(pitchespending))
    return jmpfound



def playToNextJmp(start, pitchespend, score, repcount, watchcoda,
        Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod):
    NeueSprungziele = []
#    messageBox("pitchespend vor playfromhere",str(pitchespend))
    jmp = playfromhere(start, pitchespend, score, repcount, watchcoda,
            NeueSprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
    Sprungziele.extend(NeueSprungziele)
#    messageBox("Meinscript Sprungziele", "Ziele %s Befehl %s" % (str(Sprungziele), jmp))
    if jmp == 'repend':
        # finde die letzte 'repbegin'-Marke
        SpzInd = len(Sprungziele)
        while SpzInd > 0:
            SpzInd -= 1
            Sprungziel = Sprungziele[SpzInd]
            Sprungtyp, Sprungort = Sprungziel
            if Sprungtyp == "repbegin":
                del Sprungziele[SpzInd]
                start = Sprungort
                break
        repcount += 1
        jmp = playToNextJmp(start, pitchespend, score, repcount, watchcoda,
                Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
    elif jmp == 'goon' or jmp == 'jmpcoda':
        # finde die letzte 'goonstart'-Marke
        SpzInd = len(Sprungziele)
        while SpzInd > 0:
            SpzInd -= 1
            Sprungziel = Sprungziele[SpzInd]
            Sprungtyp, Sprungort = Sprungziel
            if Sprungtyp == "goonstart":
                del Sprungziele[SpzInd]
                start = Sprungort
                break
        repcount = 1
        jmp = playToNextJmp(start, pitchespend, score, repcount, watchcoda,
                Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
    elif jmp == 'jmpvolta1':
        # finde die letzte 'goonstart'-Marke
        SpzInd = len(Sprungziele)
        while SpzInd > 0:
            SpzInd -= 1
            Sprungziel = Sprungziele[SpzInd]
            Sprungtyp, Sprungort = Sprungziel
            if Sprungtyp == "goonstart":
                del Sprungziele[SpzInd]
                start = Sprungort
                break
        repcount = 1
        jmp = playToNextJmp(start, pitchespend, score, repcount, watchcoda,
                Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
    elif jmp == 'd.S.':
        # finde die letzte 'segno'-Marke
        SpzInd = len(Sprungziele)
        while SpzInd > 0:
            SpzInd -= 1
            Sprungziel = Sprungziele[SpzInd]
            Sprungtyp, Sprungort = Sprungziel
            if Sprungtyp == "segno":
                del Sprungziele[SpzInd]
                start = Sprungort
                repcount = 1
                watchcoda = 1
                jmp = playToNextJmp(start, pitchespend, score, repcount, watchcoda,
                        Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
                break
        # keine Aktion, falls kein Segno gefunden wurde        
    elif jmp == 'd.C.':
        start = (0,0,0,0)
        repcount = 1
        watchcoda = 1
        jmp = playToNextJmp(start, pitchespend, score, repcount, watchcoda,
                Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)




# vom Cursor an losspielen
def playselected(score, descrind, checktext, killev, stopperiod):
# Zuerst die aktuelle Partitur auf die Platte schreiben, dann als minidom wieder einlesen
# - das ist eine zeitraubende Maßnahme, die es aber ermöglicht, Wiederholungen und Haltebögen
# zu implementieren, solange diese noch nicht über die Capella-Klassen direkt zugänglich sind.
# Alle Variablen, die sich auf die so angesprochenen Objekte beziehen, haben im Folgenden
# das Txt-Suffix.
    tempFile = tempfile.mktemp('.capx')
    score.write(tempFile)
    zr = zipfile.ZipFile(tempFile, 'r')
    for name in zr.namelist():
        t = zr.read(name)
        if name == 'score.xml':
            doc = xml.dom.minidom.parseString(t)
            scoreTxt = doc.documentElement
            staffLayoutsTxt = scoreTxt.getElementsByTagName('staffLayout')
            assert len(staffLayoutsTxt) > 0
            systemsTxt = getNthChildElement(scoreTxt, 'systems', 0)
            assert systemsTxt
# den Cursor innerhalb seines Systems lokalisieren
    sel = curSelection()
    start = sel[0]
    restart = start
    stop = sel[1]
# Organisiere das Überbinden von Tönen über den Zeilenumbruch und über Sprungbefehle hinaus:
# Wir brauchen ein Array von anfangs leeren Directories
    pitchespend = []
    for v in descrind:
        pitchespend.append({})
    repcount = 1
    watchcoda = 0
    Sprungziele = []
    playToNextJmp(start, pitchespend, score, repcount, watchcoda,
            Sprungziele, systemsTxt, staffLayoutsTxt, descrind, checktext, killev, zr, stopperiod)
    MidiOut.allNotesOff()
    zr.close()
    os.remove(tempFile)


# Hauptprogramm
as = activeScore()
if as:
# welche Stimmen der VoiceList gehören zu einer einzigen Zeile?
    vl = as.voiceList()
    maxvoicesinstave = []
    getmaxvoices( as, maxvoicesinstave )

    checkval = []
    checktext = []
    index = 0
    for vname in vl:
        i = maxvoicesinstave[index]
        if i == 1:
            checkval.append( (vname, i-1))
            checktext.append( CheckBox (vname, value=1))
        else:
            j = i
            while i:
                checkval.append( (vname, j - i))
                string = "%s (%d) " % (vname, j - i + 1)
                checktext.append( CheckBox (string, value=1))
                i -= 1
        index += 1
    
    choices = ['1','2','3','4','5','0']
    combo = ComboBox(choices, width=5)
    choiceslabel1 = Label('Anhalten nach jeder', width = 10)
    choiceslabel2 = Label('-ten Zeile', width = 10)
    hBox = HBox([choiceslabel1, combo, choiceslabel2],  padding=8)
    choiceslabel3 = Label('0 = gar nicht anhalten (nicht unterbrechbar!)', width = 10)
    vBox2 = VBox([hBox, choiceslabel3], padding=10)
    vBox   = VBox(checktext,    padding=12)
    lastBox = HBox([vBox, vBox2], padding=20)
    dlg = Dialog('Ausgewählte Stimmen vorspielen', lastBox )
    if dlg.run():
        killevent = threading.Event()
        stopperiod = combo.value() + 1
        if stopperiod == 6:
            stopperiod = 0
        playthr = threading.Thread(None, playselected,"play",(as, checkval, checktext, killevent, stopperiod))
        playthr.run()
#        playthr.start()
#        messageBox("MeinScript", "Vorspiel beenden")
        killevent.set()

