# -*- 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 jeder Notenzeile eine Dialogbox gezeigt, die die Wahl zwischen weiterspielen und abbrechen ermöglicht.

Beim Vorspiel werden Haltebögen nicht berücksichtigt

Wiederholungen werden nicht berücksichtigt

Die Instrumentenzuordnung im Mustersystem wird nicht berücksichtigt

Version 1: Bernd Jungmann 7.2.04
<<<
"""

import time

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
        if obj.isChord():
            return obj.time()
        else:
            if 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


# vom Cursor an losspielen
def playselected(score, descrind, checktext):
    sel = curSelection()
    start = sel[0]
    stop = sel[1]
    startmoment = getMoment(score, start)
    restart = (0, 0, 0, 0)
    
    sysnr = 0
    firstsys = 1
    for sys in as.systems():
        if firstsys and start[0] > sysnr:
            sysnr += 1
            continue
        sysnr += 1
        Tempo = sys.get('tempo')
        if 1 != messageBox("Ausgewählte Stimmen vorspielen", "jetzt müßte System %d gespielt werden im Tempo %.3f." % (sysnr, Tempo), 0, 1):
            break
# plan soll eine Liste der Tupel (moment, on, nChannel, nNote, nVolume) werden, dabei
# ist moment der laufende Zeitpunkt von Beginn des Systems an gerechnet, on ist die 
# boolesche Variable ob angeschaltet oder ausgemacht werden soll, nChannel ist der 

# MIDI-Kanal, nNote ist die Tonhöhe und nVolume die Lautstärke.
        plan = []
        defaultMeter = 1.0
        voiceind = 0
        for elem in checktext:
            MidiOut.setMainVolume(voiceind, 127)  # todo: Lautstärke aus Mustersystem holen
            MidiOut.programChange(voiceind, 10)   # todo: Instrumentierung aus Mustersystem holen
            voiceind += 1
        voiceind = 0
        while voiceind < len(checktext):
            if checktext[voiceind].value():
                descr, i = descrind[voiceind]
                istaff = sys.staffIndexFromDescr(descr)
                if istaff >= 0:
                    staff = sys.staff(istaff)
                    nvoices = staff.nVoices()
                    if i < nvoices:
                        MidiChan = voiceind % 16  # nur 16 Midikanäle möglich
                        MidiVolume = 127       # todo: aus Mustersystem holen
                        voice = staff.voice(i)
                        for noteobj in voice.noteObjs():
#                            if noteobj.isBarline():
                                # wir müssen rauskriegen, welcher typ das ist!
                                # Dann können wir Wiederholungen implementieren.
                            if noteobj.isChord():
                                moment = noteobj.time()
                                duration = noteobj.duration()
                                if firstsys:
                                    if moment < startmoment - 0.00001: # muß mit Ungenauigkeiten rechnen!
                                        continue
                                    else:
                                        moment -= startmoment
                                for head in noteobj.heads():
                                    ii = head.index()
                                    pitch = head.chromaticPitch()
                                    plan.append((moment, 1, MidiChan, pitch, MidiVolume))
                                    plan.append((moment + duration * 0.99, 0, MidiChan, pitch, MidiVolume))
            voiceind += 1
        firstsys = 0


        plan.sort()
        lastMoment = 0.0
        for playevent in plan:
            moment, on, MidiChan, pitch, MidiVolume = playevent
            towait = moment - lastMoment
# 240 = 4 Viertel * 60 Sekunden
            time.sleep(towait * 240 / Tempo )
            if on:
                MidiOut.noteOn(MidiChan, pitch, MidiVolume)
            else:
                MidiOut.noteOff(MidiChan, pitch)
            lastMoment = moment
        MidiOut.allNotesOff()


if activeScore():
    as = activeScore()
    vl = as.voiceList()
# welche Stimmen der VoiceList gehören zu einer einzigen Zeile?
    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

    vBox   = VBox(checktext,    padding=16)
    dlg = Dialog('Ausgewählte Stimmen vorspielen', vBox )
    if dlg.run():
        playselected(as, checkval, checktext)

