Example #1
0
    def expandFingerTremolo(self, token1: HumdrumToken, token2: HumdrumToken):
        if token2 is None:
            return

        m = re.search(r'@@(\d+)@@', token1.text)
        if not m:
            return

        value: int = int(m.group(1))
        if not Convert.isPowerOfTwo(HumNum(value)):
            print(f'Error: not a power of two: {token1}', file=sys.stderr)
            return
        if value < 8:
            print(
                f'Error: tremolo can only be eighth-notes or shorter: {token1}',
                file=sys.stderr)
            return

        duration: HumNum = Convert.recipToDuration(token1.text)
        count: HumNum = duration

        count *= value
        count /= 4
        if count.denominator != 1:
            print(
                f'Error: tremolo repetition count must be an integer: {token1}',
                file=sys.stderr)
            return
        increment: HumNum = HumNum(4)
        increment /= value

        tnotes: int = count.numerator * 2

        self.storeFirstTremoloNoteInfo(token1)

        beams: int = int(math.log(float(value), 2)) - 2
        markup: str = f'@@{value}@@'
        base1: str = token1.text
        base1 = re.sub(markup, '', base1)
        # Currently not allowed to add tremolo to beamed notes, so remove all beaming:
        base1 = re.sub(r'[LJKk]+', '', base1)
        startBeam: str = 'L' * beams
        endBeam: str = 'J' * beams

        # Set the rhythm of the tremolo notes.
        # Augmentation dot is expected adjacent to regular rhythm value.
        # Maybe allow anywhere?
        base1 = re.sub(r'\d+%?\d*\.*', str(value), base1)
        initial: str = base1 + startBeam
        # remove slur end from start of tremolo
        initial = re.sub(r'[)]+[<>]?', '', initial)

        # remove slur information from middle of tremolo
        base1 = re.sub(r'[()]+[<>]?', '', base1)

        token1.text = initial
        token1.ownerLine.createLineFromTokens()

        base2: str = token2.text
        base2 = re.sub(markup, '', base2)
        base2 = re.sub(r'[LJKk]+', '', base2)
        base2 = re.sub(r'\d+%?\d*\.*', str(value), base2)

        terminal: str = base2 + endBeam
        # remove slur start information from end of tremolo:
        terminal = re.sub(r'[(]+[<>]?', '', terminal)

        state: bool = False

        # Now fill in the rest of the tremolos.
        startTime: HumNum = token1.durationFromStart
        timestamp: HumNum = startTime + increment
        currTok: HumdrumToken = token1.nextToken(0)
        counter: int = 1
        while currTok is not None:
            if not currTok.isData:
                currTok = currTok.nextToken(0)
                continue

            cstamp: HumNum = currTok.durationFromStart
            if cstamp < timestamp:
                currTok = currTok.nextToken(0)
                continue

            if cstamp > timestamp:
                print('\tWarning: terminating tremolo insertion early',
                      file=sys.stderr)
                print(f'\tCSTAMP : {cstamp} TSTAMP : {timestamp}',
                      file=sys.stderr)
                break

            counter += 1
            if counter == tnotes:
                currTok.text = terminal
                self.storeLastTremoloNoteInfo(currTok)
            else:
                if state:
                    currTok.text = base1
                else:
                    currTok.text = base2
                state = not state

            currTok.ownerLine.createLineFromTokens()
            if counter >= tnotes:
                # done with inserting of tremolo notes
                break

            timestamp += increment
            currTok = currTok.nextToken(0)
Example #2
0
    def expandTremolo(self, token: HumdrumToken):
        value: int = 0
        addBeam: bool = False
        tnotes: int = -1

        m = re.search(r'@(\d+)@', token.text)
        if not m:
            return

        value = int(m.group(1))
        duration: HumNum = Convert.recipToDuration(token.text)
        count: HumNum = HumNum(duration * value / 4)
        if count.denominator != 1:
            print(f'Error: non-integer number of tremolo notes: {token}',
                  file=sys.stderr)
            return
        if value < 8:
            print(
                f'Error: tremolo notes can only be eighth-notes or shorter: {token}',
                file=sys.stderr)
            return
        if float(duration) > 0.5:
            # needs to be less than one for tuplet quarter note tremolos
            addBeam = True

        # There are cases where duration < 1 need added beams
        # when the note is not already in a beam.  Such as
        # a plain 8th note with a slash.  This needs to be
        # converted into two 16th notes with a beam so that
        # *tremolo can reduce it back into a tremolo, since
        # it will only reduce beam groups.

        repeat: HumNum = duration
        repeat *= value
        repeat /= 4
        increment: HumNum = HumNum(4)
        increment /= value
        if repeat.denominator != 1:
            print(
                f'Error: tremolo repetition count must be an integer: {token}',
                file=sys.stderr)
            return
        tnotes = repeat.numerator

        self.storeFirstTremoloNoteInfo(token)

        beams: int = int(math.log(float(value), 2)) - 2
        markup: str = f'@{value.numerator}@'
        base: str = re.sub(markup, '', token.text)

        # complicated beamings are not allowed yet (no internal L/J markers in tremolo beam)
        hasBeamStart: bool = 'L' in base
        hasBeamStop: bool = 'J' in base

        if addBeam:
            hasBeamStart = True
            hasBeamStop = True

        # Currently not allowed to add tremolo to beamed notes, so remove all beaming:
        base = re.sub(r'[LJKk]+', '', base)
        startBeam: str = 'L' * beams
        endBeam: str = 'J' * beams

        # Set the rhythm of the tremolo notes.
        # Augmentation dot is expected adjacent to regular rhythm value.
        # Maybe allow anywhere?
        base = re.sub(r'\d+%?\d*\.*', str(value.numerator), base)
        initial: str = base
        if hasBeamStart:
            initial += startBeam
        terminal: str = base
        if hasBeamStop:
            terminal += endBeam

        # remove slur end from start of tremolo:
        terminal = re.sub(r'[(]+[<>]', '', terminal)

        token.text = initial
        token.ownerLine.createLineFromTokens()

        # Now fill in the rest of the tremolos.
        startTime: HumNum = token.durationFromStart
        timestamp: HumNum = startTime + increment
        currTok: HumdrumToken = token.nextToken(0)
        counter: int = 1

        while currTok is not None:
            if not currTok.isData:
                currTok = currTok.nextToken(0)
                continue

            duration: HumNum = currTok.ownerLine.duration
            if duration == 0:
                # grace note line, so skip
                currTok = currTok.nextToken(0)
                continue

            cstamp: HumNum = currTok.durationFromStart
            if cstamp < timestamp:
                currTok = currTok.nextToken(0)
                continue

            if cstamp > timestamp:
                print('\tWarning: terminating tremolo insertion early',
                      file=sys.stderr)
                print(f'\tCSTAMP : {cstamp} TSTAMP : {timestamp}',
                      file=sys.stderr)
                break

            counter += 1
            if counter == tnotes:
                currTok.text = terminal
                self.storeLastTremoloNoteInfo(currTok)
            else:
                currTok.text = base
            currTok.ownerLine.createLineFromTokens()
            if counter >= tnotes:
                # done with inserting of tremolo notes.
                break

            timestamp += increment
            currTok = currTok.nextToken(0)