コード例 #1
0
    def __onMoveTo(self, where: MoveTo) -> None:
        if self.__player_id is None:
            logger.warning("Player action without active player.")
            return

        if where == MoveTo.Start:
            self.setCurrentTime(audioproc.MusicalTime())

        elif where == MoveTo.End:
            self.setCurrentTime(self.time_mapper.end_time)

        elif where == MoveTo.PrevBeat:
            beat = int(
                (self.__current_time + audioproc.MusicalDuration(3, 16))
                / audioproc.MusicalTime(1, 4))
            new_time = audioproc.MusicalTime(beat - 1, 4)
            if new_time < audioproc.MusicalTime(0, 1):
                new_time = audioproc.MusicalTime(0, 1)
            self.setCurrentTime(new_time)

        elif where == MoveTo.NextBeat:
            beat = int(
                (self.__current_time + audioproc.MusicalDuration(3, 16))
                / audioproc.MusicalTime(1, 4))
            new_time = audioproc.MusicalTime(beat + 1, 4)
            if new_time > self.time_mapper.end_time:
                new_time = self.time_mapper.end_time
            self.setCurrentTime(new_time)

        else:
            raise ValueError(where)
コード例 #2
0
 def max_allowed_dots(self) -> int:
     base_duration = self.base_duration
     if base_duration <= audioproc.MusicalDuration(1, 32):
         return 0
     if base_duration <= audioproc.MusicalDuration(1, 16):
         return 1
     if base_duration <= audioproc.MusicalDuration(1, 8):
         return 2
     return 3
コード例 #3
0
    async def test_create_note(self):
        track = await self._add_track()
        measure = track.measure_list[0].measure
        self.assertEqual(len(measure.notes), 0)

        with self.project.apply_mutations('test'):
            measure.create_note(0, value_types.Pitch('F2'),
                                audioproc.MusicalDuration(1, 4))
        self.assertEqual(len(measure.notes), 1)
        self.assertEqual(measure.notes[0].pitches[0], value_types.Pitch('F2'))
        self.assertEqual(measure.notes[0].base_duration,
                         audioproc.MusicalDuration(1, 4))
コード例 #4
0
    async def test_add_beat(self):
        track = await self._add_track()
        measure = track.measure_list[0].measure

        with self.project.apply_mutations('test'):
            measure.create_beat(audioproc.MusicalDuration(1, 4))
        self.assertEqual(measure.beats[0].time, audioproc.MusicalDuration(1, 4))
        self.assertEqual(measure.beats[0].velocity, 100)

        with self.project.apply_mutations('test'):
            measure.create_beat(audioproc.MusicalDuration(2, 4), 120)
        self.assertEqual(measure.beats[1].time, audioproc.MusicalDuration(2, 4))
        self.assertEqual(measure.beats[1].velocity, 120)
コード例 #5
0
    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)

        self.__grid_step = audioproc.MusicalDuration(1, 1)

        self.scaleXChanged.connect(self.__scaleXChanged)
        self.__scaleXChanged(self.scaleX())
コード例 #6
0
 def __scaleXChanged(self, scale_x: fractions.Fraction) -> None:
     self.__grid_step = audioproc.MusicalDuration(1, 64)
     min_dist = 96
     while int(self.__grid_step * scale_x) <= min_dist:
         self.__grid_step *= 2
         if int(self.__grid_step) > 1:
             min_dist = 36
コード例 #7
0
ファイル: node_ui.py プロジェクト: wanderer6994/noisicaa
 def __durationChanged(
         self, change: music.PropertyValueChange[audioproc.MusicalDuration]
 ) -> None:
     num_beats = change.new_value / audioproc.MusicalDuration(1, 4)
     assert num_beats.denominator == 1
     self.__duration.setValue(num_beats.numerator)
     self.__pianoroll.setDuration(change.new_value)
コード例 #8
0
ファイル: model.py プロジェクト: wanderer6994/noisicaa
 def create_beat(self,
                 time: audioproc.MusicalDuration,
                 velocity: int = 100) -> Beat:
     assert audioproc.MusicalDuration(0, 1) <= time < self.duration
     assert 0 <= velocity <= 127
     beat = self._pool.create(Beat, time=time, velocity=velocity)
     self.beats.append(beat)
     return beat
コード例 #9
0
ファイル: node_ui.py プロジェクト: wanderer6994/noisicaa
    def __durationEdited(self, beats: int) -> None:
        duration = audioproc.MusicalDuration(beats, 4)
        if duration == self.__node.duration:
            return

        with self.project.apply_mutations('%s: Change duration' %
                                          self.__node.name):
            self.__node.set_duration(duration)
コード例 #10
0
ファイル: node_ui.py プロジェクト: wanderer6994/noisicaa
    def __init__(self, node: model.MidiLooper, session_prefix: str,
                 **kwargs: Any) -> None:
        super().__init__(**kwargs)

        self.__node = node

        self.__visible = False

        self.__slot_connections = slots.SlotConnectionManager(
            session_prefix='midi_looper:%016x:%s' %
            (self.__node.id, session_prefix),
            context=self.context)
        self.add_cleanup_function(self.__slot_connections.cleanup)

        self.__listeners = core.ListenerMap[str]()
        self.add_cleanup_function(self.__listeners.cleanup)

        self.__listeners[
            'node-messages'] = self.audioproc_client.node_messages.add(
                '%016x' % self.__node.id, self.__nodeMessage)

        self.__event_map = []  # type: List[int]
        self.__recorded_events = []  # type: List[value_types.MidiEvent]

        self.__duration = QtWidgets.QSpinBox()
        self.__duration.setObjectName('duration')
        self.__duration.setSuffix(" beats")
        self.__duration.setKeyboardTracking(False)
        self.__duration.setRange(1, 100)
        num_beats = self.__node.duration / audioproc.MusicalDuration(1, 4)
        assert num_beats.denominator == 1
        self.__duration.setValue(num_beats.numerator)
        self.__duration.valueChanged.connect(self.__durationEdited)
        self.__listeners['duration'] = self.__node.duration_changed.add(
            self.__durationChanged)

        self.__record = RecordButton()
        self.__record.clicked.connect(self.__recordClicked)

        self.__pianoroll = pianoroll.PianoRoll()
        self.__pianoroll.setDuration(self.__node.duration)

        for event in self.__node.patches[0].events:
            self.__event_map.append(self.__pianoroll.addEvent(event))
        self.__listeners['events'] = self.__node.patches[0].events_changed.add(
            self.__eventsChanged)

        l2 = QtWidgets.QHBoxLayout()
        l2.setContentsMargins(0, 0, 0, 0)
        l2.addWidget(self.__record)
        l2.addWidget(self.__duration)
        l2.addStretch(1)

        l1 = QtWidgets.QVBoxLayout()
        l1.setContentsMargins(0, 0, 0, 0)
        l1.addLayout(l2)
        l1.addWidget(self.__pianoroll)
        self.setLayout(l1)
コード例 #11
0
    async def test_delete_beat(self):
        track = await self._add_track()
        measure = track.measure_list[0].measure
        with self.project.apply_mutations('test'):
            measure.create_beat(audioproc.MusicalDuration(1, 4))

        with self.project.apply_mutations('test'):
            measure.delete_beat(measure.beats[0])
        self.assertEqual(len(measure.beats), 0)
コード例 #12
0
ファイル: model.py プロジェクト: wanderer6994/noisicaa
 def _create_events(
         self, time: audioproc.MusicalTime, measure: base_track.Measure
 ) -> Iterator[base_track.PianoRollInterval]:
     measure = down_cast(BeatMeasure, measure)
     for beat in measure.beats:
         beat_time = time + beat.time
         event = base_track.PianoRollInterval(
             beat_time, beat_time + audioproc.MusicalDuration(1, 4),
             self._node.pitch, 127)
         yield event
コード例 #13
0
 def duration(self) -> audioproc.MusicalDuration:
     duration = self.base_duration
     dots = self.dots
     tuplet = self.tuplet
     for _ in range(dots):
         duration *= fractions.Fraction(3, 2)
     if tuplet == 3:
         duration *= fractions.Fraction(2, 3)
     elif tuplet == 5:
         duration *= fractions.Fraction(4, 5)
     return audioproc.MusicalDuration(duration)
コード例 #14
0
    def __update(self) -> None:
        if self.displayMode() == self.DisplayMode.MusicalTime:
            beat = self.__current_time / audioproc.MusicalDuration(1, 4)
            self.display('%.3f' % beat)

        else:
            assert self.displayMode() == self.DisplayMode.RealTime
            t = (self.__time_mapper.musical_to_sample_time(self.__current_time)
                 / self.__time_mapper.sample_rate)
            millis = int(1000 * t) % 1000
            seconds = int(t) % 60
            minutes = int(t) // 60
            self.display('%d:%02d.%03d' % (minutes, seconds, millis))
コード例 #15
0
ファイル: node_ui_test.py プロジェクト: wanderer6994/noisicaa
    async def test_duration(self):
        widget = node_ui.MidiLooperNodeWidget(node=self.node,
                                              session_prefix='test',
                                              context=self.context)
        try:
            duration = widget.findChild(QtWidgets.QSpinBox, 'duration')
            assert duration is not None
            self.assertEqual(duration.value(),
                             (self.node.duration /
                              audioproc.MusicalDuration(1, 4)).numerator)

            with self.project.apply_mutations('test'):
                self.node.set_duration(audioproc.MusicalDuration(5, 4))
            self.assertEqual(audioproc.MusicalDuration(duration.value(), 4),
                             self.node.duration)

            duration.setValue(7)
            self.assertEqual(self.node.duration,
                             audioproc.MusicalDuration(7, 4))

        finally:
            widget.cleanup()
コード例 #16
0
    def create(self,
               *,
               pitches: Optional[Iterable[value_types.Pitch]] = None,
               base_duration: Optional[audioproc.MusicalDuration] = None,
               dots: int = 0,
               tuplet: int = 0,
               **kwargs: Any) -> None:
        super().create(**kwargs)

        if pitches is not None:
            self.pitches.extend(pitches)
        if base_duration is None:
            base_duration = audioproc.MusicalDuration(1, 4)
        self.base_duration = base_duration
        self.dots = dots
        self.tuplet = tuplet

        assert (self.base_duration.numerator == 1
                and self.base_duration.denominator in (1, 2, 4, 8, 16, 32)), \
            self.base_duration
コード例 #17
0
    def __onAppendMeasuresDone(self, buttons: QtWidgets.QButtonGroup,
                               count_input: QtWidgets.QSpinBox) -> None:
        track = self.track.track
        if buttons.checkedId() == 1:
            with self.project.apply_mutations('%s: Fill track to end' %
                                              track.name):
                duration = audioproc.MusicalDuration()
                for mref in track.measure_list:
                    duration += mref.measure.duration

                cnt = int((self.project.duration - duration) /
                          track.measure_list[-1].measure.duration)
                for _ in range(cnt):
                    track.append_measure()

        else:
            assert buttons.checkedId() == 2
            cnt = count_input.value()
            with self.project.apply_mutations('%s: Append %d measures' %
                                              (track.name, cnt)):
                for _ in range(cnt):
                    track.append_measure()
コード例 #18
0
ファイル: track_ui.py プロジェクト: wanderer6994/noisicaa
    def paintForeground(self, painter: QtGui.QPainter) -> None:
        ymid = self.height() // 2

        for beat in self.measure.beats:
            if not audioproc.MusicalDuration(0, 1) <= beat.time < self.measure.duration:
                logger.warning(
                    "Beat outside of measure: %s not in [0,%s)",
                    beat.time, self.measure.duration)
                continue

            pos = int(self.width() * (beat.time / self.measure.duration).fraction)

            painter.fillRect(
                pos + 2, ymid + 8, 4, 22 * beat.velocity // 127,
                QtGui.QColor(255, 200, 200))

            painter.setPen(Qt.NoPen)
            painter.setBrush(Qt.black)
            polygon = QtGui.QPolygon()
            polygon.append(QtCore.QPoint(pos, ymid - 12))
            polygon.append(QtCore.QPoint(pos, ymid + 12))
            polygon.append(QtCore.QPoint(pos + 8, ymid))
            painter.drawPolygon(polygon)
コード例 #19
0
class ScoreMeasureEditor(measured_track_editor.MeasureEditor):
    FOREGROUND = 'fg'
    BACKGROUND = 'bg'
    GHOST = 'ghost'

    layers = [
        BACKGROUND,
        measured_track_editor.MeasureEditor.PLAYBACK_POS,
        FOREGROUND,
        GHOST,
    ]

    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)

        self._edit_areas = []  # type: List[Tuple[int, int, int, bool]]
        self._note_area = None  # type: Tuple[int, int]
        self.__mouse_pos = None  # type: QtCore.QPoint
        self.__ghost_pos = None  # type: QtCore.QPoint

        self.track_editor.currentToolChanged.connect(
            lambda _: self.updateGhost(self.__mouse_pos))

    @property
    def track_editor(self) -> 'ScoreTrackEditor':
        return down_cast(ScoreTrackEditor, super().track_editor)

    @property
    def track(self) -> model.ScoreTrack:
        return down_cast(model.ScoreTrack, super().track)

    @property
    def measure(self) -> model.ScoreMeasure:
        return down_cast(model.ScoreMeasure, super().measure)

    _accidental_map = {
        '': 'accidental-natural',
        '#': 'accidental-sharp',
        'b': 'accidental-flat',
        '##': 'accidental-double-sharp',
        'bb': 'accidental-double-flat',
    }

    def addMeasureListeners(self) -> None:
        self._measure_listeners.add(
            self.measure.content_changed.add(
                lambda _=None: self.invalidatePaintCache(self.FOREGROUND))
        )  # type: ignore[misc]
        self._measure_listeners.add(
            self.measure.clef_changed.add(self.onClefChanged))
        self._measure_listeners.add(
            self.measure.key_signature_changed.add(self.onKeySignatureChanged))

    def onClefChanged(
            self, change: music.PropertyValueChange[value_types.Clef]) -> None:
        self.invalidatePaintCache(self.BACKGROUND, self.FOREGROUND)
        self.next_sibling.invalidatePaintCache(self.BACKGROUND,
                                               self.FOREGROUND)

    def onKeySignatureChanged(
            self, change: music.PropertyValueChange[value_types.KeySignature]
    ) -> None:
        self.invalidatePaintCache(self.BACKGROUND, self.FOREGROUND)
        self.next_sibling.invalidatePaintCache(self.BACKGROUND,
                                               self.FOREGROUND)

    def paintLayer(self, layer: str, painter: QtGui.QPainter) -> None:
        if layer == self.BACKGROUND:
            self.paintBackground(painter)
        elif layer == self.FOREGROUND:
            self.paintForeground(painter)
        elif layer == self.GHOST:
            self.paintGhost(painter)

    def paintBackground(self, painter: QtGui.QPainter) -> None:
        ymid = self.height() // 2

        painter.setPen(Qt.black)
        painter.setBrush(Qt.black)

        for l in range(-2, 3):
            painter.drawLine(0, ymid + 20 * l, self.width() - 1, ymid + 20 * l)

        if self.is_first:
            painter.fillRect(0, ymid - 40, 2, 20 * 4, Qt.black)

        painter.drawLine(self.width() - 1, ymid - 40,
                         self.width() - 1, ymid + 40)

        if not self.measure_reference.is_first:
            prev_sibling = down_cast(
                model.ScoreMeasure,
                self.measure_reference.prev_sibling.measure)
        else:
            prev_sibling = None

        base_stave_line = self.measure.clef.center_pitch.stave_line
        base_octave = self.measure.clef.base_octave
        x = 0

        paint_clef = prev_sibling is None or self.measure.clef != prev_sibling.clef

        if paint_clef and self.width() - x > 200:
            svg_symbol.paintSymbol(
                painter, 'clef-%s' % self.measure.clef.symbol,
                QtCore.QPoint(
                    x + 30, ymid - 10 *
                    (self.measure.clef.base_pitch.stave_line - base_stave_line)
                ))
            x += 60

        acc_map = {
            'C#': 'C%d' % (base_octave + 1),
            'D#': 'D%d' % (base_octave + 1),
            'E#': 'E%d' % (base_octave + 1),
            'F#': 'F%d' % (base_octave + 1),
            'G#': 'G%d' % (base_octave + 1),
            'A#': 'A%d' % base_octave,
            'B#': 'B%d' % base_octave,
            'Cb': 'C%d' % (base_octave + 1),
            'Db': 'D%d' % (base_octave + 1),
            'Eb': 'E%d' % (base_octave + 1),
            'Fb': 'F%d' % base_octave,
            'Gb': 'G%d' % base_octave,
            'Ab': 'A%d' % base_octave,
            'Bb': 'B%d' % base_octave,
        }

        paint_key_signature = (
            prev_sibling is None
            or self.measure.key_signature != prev_sibling.key_signature)

        if paint_key_signature and self.width() - x > 200:
            for acc in self.measure.key_signature.accidentals:
                value = acc_map[acc]
                stave_line = value_types.Pitch(
                    value).stave_line - base_stave_line

                svg_symbol.paintSymbol(
                    painter, self._accidental_map[acc[1:]],
                    QtCore.QPoint(x + 10, ymid - 10 * stave_line))

                x += 10

            if self.measure.key_signature.accidentals:
                x += 10

        paint_time_signature = (
            prev_sibling is None
            or self.measure.time_signature != prev_sibling.time_signature)

        if paint_time_signature and self.width() - x > 200:
            font = QtGui.QFont('FreeSerif', 30, QtGui.QFont.Black)
            font.setStretch(120)
            painter.setFont(font)

            painter.drawText(x, ymid - 5,
                             '%d' % self.measure.time_signature.upper)
            painter.drawText(x, ymid + 32,
                             '%d' % self.measure.time_signature.lower)

            x += 40

        if self.width() - x > 100:
            self._note_area = (x + 20, self.width() - x - 20)

        else:
            self._note_area = (0, self.width())

    def paintForeground(self, painter: QtGui.QPainter) -> None:
        assert self._note_area is not None
        self._edit_areas.clear()

        ymid = self.height() // 2

        base_stave_line = self.measure.clef.center_pitch.stave_line
        base_octave = self.measure.clef.base_octave

        acc_map = {
            'C#': 'C%d' % (base_octave + 1),
            'D#': 'D%d' % (base_octave + 1),
            'E#': 'E%d' % (base_octave + 1),
            'F#': 'F%d' % (base_octave + 1),
            'G#': 'G%d' % (base_octave + 1),
            'A#': 'A%d' % base_octave,
            'B#': 'B%d' % base_octave,
            'Cb': 'C%d' % (base_octave + 1),
            'Db': 'D%d' % (base_octave + 1),
            'Eb': 'E%d' % (base_octave + 1),
            'Fb': 'F%d' % base_octave,
            'Gb': 'G%d' % base_octave,
            'Ab': 'A%d' % base_octave,
            'Bb': 'B%d' % base_octave,
        }

        active_accidentals = {}
        for acc in self.measure.key_signature.accidentals:
            value = acc_map[acc]
            active_accidentals[value[:1]] = acc[1:]

        x, note_area_width = self._note_area
        if note_area_width > 80:
            px = x - 20
            note_time = audioproc.MusicalDuration(0)
            for idx, note in enumerate(self.measure.notes):
                overflow = note_time + note.duration > self.measure.duration

                if note.is_rest:
                    sym = {
                        1: 'rest-whole',
                        2: 'rest-half',
                        4: 'rest-quarter',
                        8: 'rest-8th',
                        16: 'rest-16th',
                        32: 'rest-32th',
                    }[note.base_duration.denominator]
                    svg_symbol.paintSymbol(painter, sym,
                                           QtCore.QPoint(x, ymid))

                    if note.base_duration >= audioproc.MusicalDuration(1, 2):
                        dx = 25
                        dy = -10
                    else:
                        dx = 12
                        dy = 0

                    for d in range(note.dots):
                        painter.setPen(Qt.NoPen)
                        painter.setBrush(Qt.black)
                        painter.drawEllipse(dx - 4 + 10 * d, dy - 4, 9, 9)

                    if note.tuplet != 0:
                        painter.setPen(Qt.black)
                        painter.drawText(-5, -45, '%d' % note.tuplet)

                    # if overflow:
                    #     n.setOpacity(0.4)
                elif len(note.pitches) > 0:
                    min_stave_line = 1000
                    max_stave_line = -1000

                    for pitch in note.pitches:
                        stave_line = pitch.stave_line - base_stave_line
                        min_stave_line = min(min_stave_line, stave_line)
                        max_stave_line = max(max_stave_line, stave_line)

                    painter.setPen(Qt.black)
                    painter.setOpacity(0.4 if overflow else 0.8)

                    # Ledger lines above stave.
                    for l in range(6, max_stave_line + 1, 2):
                        painter.drawLine(x - 20, ymid - 10 * l, x + 20,
                                         ymid - 10 * l)

                    # Ledger lines below stave.
                    for l in range(-6, min_stave_line - 1, -2):
                        painter.drawLine(x - 20, ymid - 10 * l, x + 20,
                                         ymid - 10 * l)

                    painter.setOpacity(1.0)

                    for pitch in note.pitches:
                        stave_line = pitch.stave_line - base_stave_line

                        y = ymid - 10 * stave_line

                        active_accidental = active_accidentals.get(
                            pitch.value, '')
                        if pitch.accidental != active_accidental:
                            sym = self._accidental_map[pitch.accidental]
                            svg_symbol.paintSymbol(painter, sym,
                                                   QtCore.QPoint(x - 12, y))
                            active_accidentals[pitch.value] = pitch.accidental

                        if note.base_duration >= audioproc.MusicalDuration(
                                1, 2):
                            svg_symbol.paintSymbol(painter, 'note-head-void',
                                                   QtCore.QPoint(x, y))
                        else:
                            svg_symbol.paintSymbol(painter, 'note-head-black',
                                                   QtCore.QPoint(x, y))

                        if note.base_duration <= audioproc.MusicalDuration(
                                1, 2):
                            painter.fillRect(x + 8, y - 63, 3, 60, Qt.black)

                        if note.base_duration == audioproc.MusicalDuration(
                                1, 8):
                            flags = 1
                        elif note.base_duration == audioproc.MusicalDuration(
                                1, 16):
                            flags = 2
                        elif note.base_duration == audioproc.MusicalDuration(
                                1, 32):
                            flags = 3
                        else:
                            flags = 0

                        for f in range(flags):
                            svg_symbol.paintSymbol(
                                painter, 'note-flag-down',
                                QtCore.QPoint(x + 11, y - 63 + 12 * f))

                        for d in range(note.dots):
                            painter.setPen(Qt.NoPen)
                            painter.setBrush(Qt.black)
                            painter.drawEllipse(x + 12 + 10 * d, y - 4, 9, 9)

                        if note.tuplet != 0:
                            painter.drawText(x - 5, y - 85, '%d' % note.tuplet)

                    # if overflow:
                    #     n.setOpacity(0.4)

                x1 = max(x - 12, px)
                x2 = max(x + 13, x1)
                if x1 > px:
                    self._edit_areas.append((px, x1, idx, False))
                    px = x1
                if x2 > x1:
                    self._edit_areas.append((x1, x2, idx, True))
                    px = x2

                note_time += note.duration
                x += int(note_area_width * note.duration.fraction)

            if px < self.width():
                self._edit_areas.append(
                    (px, self.width(), len(self.measure.notes), False))
        else:
            self._note_area = (0, self.width())

    def paintGhost(self, painter: QtGui.QPainter) -> None:
        if self.__ghost_pos is None:
            return

        ymid = self.height() // 2

        tool = self.track_editor.currentToolType()
        pos = self.__ghost_pos
        painter.setOpacity(0.4)

        if tool.is_rest:
            sym = {
                1: 'rest-whole',
                2: 'rest-half',
                4: 'rest-quarter',
                8: 'rest-8th',
                16: 'rest-16th',
                32: 'rest-32th',
            }[self._tool_duration_map[tool].denominator]
            svg_symbol.paintSymbol(painter, sym, QtCore.QPoint(pos.x(), ymid))

        elif tool.is_note:
            duration = self._tool_duration_map[tool]

            if duration >= audioproc.MusicalDuration(1, 2):
                svg_symbol.paintSymbol(painter, 'note-head-void', pos)
            else:
                svg_symbol.paintSymbol(painter, 'note-head-black', pos)

            if duration <= audioproc.MusicalDuration(1, 2):
                painter.fillRect(pos.x() + 8, pos.y() - 63, 3, 60, Qt.black)

            if duration == audioproc.MusicalDuration(1, 8):
                flags = 1
            elif duration == audioproc.MusicalDuration(1, 16):
                flags = 2
            elif duration == audioproc.MusicalDuration(1, 32):
                flags = 3
            else:
                flags = 0

            for f in range(flags):
                svg_symbol.paintSymbol(
                    painter, 'note-flag-down',
                    QtCore.QPoint(pos.x() + 11,
                                  pos.y() - 63 + 12 * f))

        elif tool.is_accidental:
            accidental = {
                tools.ToolType.ACCIDENTAL_NATURAL: '',
                tools.ToolType.ACCIDENTAL_FLAT: 'b',
                tools.ToolType.ACCIDENTAL_SHARP: '#',
                tools.ToolType.ACCIDENTAL_DOUBLE_FLAT: 'bb',
                tools.ToolType.ACCIDENTAL_DOUBLE_SHARP: '##',
            }[tool]
            sym = self._accidental_map[accidental]
            svg_symbol.paintSymbol(painter, sym, pos)

        else:
            painter.setPen(Qt.NoPen)
            painter.setBrush(Qt.black)
            painter.drawEllipse(pos.x() - 15, pos.y() - 15, 31, 31)

    def paintPlaybackPos(self, painter: QtGui.QPainter) -> None:
        assert self._note_area is not None

        left, width = self._note_area
        pos = left + int(width *
                         (self.playbackPos() / self.measure.duration).fraction)
        painter.fillRect(pos, 0, 2, self.height(), QtGui.QColor(0, 0, 160))

    def getEditArea(self, x: int) -> Tuple[int, bool, int]:
        for x1, x2, idx, overwrite in self._edit_areas:
            if x1 < x <= x2:
                return idx, overwrite, (x1 + x2) // 2
        return -1, False, 0

    _tool_duration_map = {
        tools.ToolType.NOTE_WHOLE: audioproc.MusicalDuration(1, 1),
        tools.ToolType.NOTE_HALF: audioproc.MusicalDuration(1, 2),
        tools.ToolType.NOTE_QUARTER: audioproc.MusicalDuration(1, 4),
        tools.ToolType.NOTE_8TH: audioproc.MusicalDuration(1, 8),
        tools.ToolType.NOTE_16TH: audioproc.MusicalDuration(1, 16),
        tools.ToolType.NOTE_32TH: audioproc.MusicalDuration(1, 32),
        tools.ToolType.REST_WHOLE: audioproc.MusicalDuration(1, 1),
        tools.ToolType.REST_HALF: audioproc.MusicalDuration(1, 2),
        tools.ToolType.REST_QUARTER: audioproc.MusicalDuration(1, 4),
        tools.ToolType.REST_8TH: audioproc.MusicalDuration(1, 8),
        tools.ToolType.REST_16TH: audioproc.MusicalDuration(1, 16),
        tools.ToolType.REST_32TH: audioproc.MusicalDuration(1, 32),
    }

    def durationForTool(self,
                        tool: tools.ToolType) -> audioproc.MusicalDuration:
        assert tool.is_note or tool.is_rest
        return self._tool_duration_map[tool]

    def setGhost(self, pos: QtCore.QPoint) -> None:
        if pos == self.__ghost_pos:
            return

        self.__ghost_pos = pos
        self.invalidatePaintCache(self.GHOST)

    def updateGhost(self, pos: QtCore.QPoint) -> None:
        if pos is None:
            self.setGhost(None)
            return

        ymid = self.height() // 2
        stave_line = int(ymid + 5 - pos.y()
                         ) // 10 + self.measure.clef.center_pitch.stave_line

        idx, overwrite, insert_x = self.getEditArea(pos.x())
        if idx < 0:
            self.setGhost(None)
            return

        tool = self.track_editor.currentToolType()
        if tool.is_note or tool.is_rest:
            self.setGhost(
                QtCore.QPoint(
                    insert_x, ymid - 10 *
                    (stave_line - self.measure.clef.center_pitch.stave_line)))

        elif tool.is_accidental and overwrite:
            self.setGhost(
                QtCore.QPoint(
                    insert_x - 12, ymid - 10 *
                    (stave_line - self.measure.clef.center_pitch.stave_line)))

        else:
            self.setGhost(None)

    def leaveEvent(self, evt: QtCore.QEvent) -> None:
        self.__mouse_pos = None
        self.setGhost(None)
        super().leaveEvent(evt)
コード例 #20
0
    def create(self, num: int, pos: value_types.Pos2F) -> None:
        # pylint: disable=import-outside-toplevel

        mixer_node = self.project.create_node(
            'builtin://mixer',
            name='Track #%d' % num,
            graph_pos=pos)
        mixer_node.set_control_value('gain', -10.0 * random.random())
        mixer_node.set_control_value('pan', 2.0 * random.random() - 1.0)
        self.project.create_node_connection(
            mixer_node, 'out:left',
            self.master_mixer_node, 'in:left')
        self.project.create_node_connection(
            mixer_node, 'out:right',
            self.master_mixer_node, 'in:right')

        from noisicaa.builtin_nodes.instrument import model as instrument

        instr_node = cast(
            instrument.Instrument,
            self.project.create_node(
                'builtin://instrument',
                graph_pos=pos - value_types.Pos2F(400, 0)))
        instr_node.name, instr_node.instrument_uri = self.get_instrument()
        self.project.create_node_connection(
            instr_node, 'out:left',
            mixer_node, 'in:left')
        self.project.create_node_connection(
            instr_node, 'out:right',
            mixer_node, 'in:right')

        from noisicaa.builtin_nodes.score_track import model as score_track

        track_node = cast(
            score_track.ScoreTrack,
            self.project.create_node(
                'builtin://score-track',
                name='Track #%d' % num,
                num_measures=0,
                graph_pos=pos - value_types.Pos2F(800, 0)))
        self.project.create_node_connection(
            track_node, 'out',
            instr_node, 'in')

        note_durations = (
            [audioproc.MusicalDuration(1, 4)] * 10
            + [audioproc.MusicalDuration(1, 8)] * 7
            + [audioproc.MusicalDuration(1, 16)] * 5
            + [audioproc.MusicalDuration(1, 32)] * 2
            + [audioproc.MusicalDuration(1, 2)] * 2
            + [audioproc.MusicalDuration(1, 1)] * 1
        )

        while track_node.duration < self.project.duration:
            measure = cast(score_track.ScoreMeasure, track_node.append_measure())
            duration_left = measure.duration
            while duration_left > audioproc.MusicalDuration(0, 1):
                durations = [d for d in note_durations if d <= duration_left]
                if not durations:
                    break

                note = measure.create_note(
                    index=len(measure.notes),
                    pitch=value_types.Pitch.from_midi(max(30, min(90, int(random.gauss(60, 5))))),
                    duration=random.choice(durations))

                duration_left -= note.duration
コード例 #21
0
ファイル: model.py プロジェクト: wanderer6994/noisicaa
    def create(self, **kwargs: Any) -> None:
        super().create(**kwargs)

        self.duration = audioproc.MusicalDuration(8, 4)
        self.patches.append(self._pool.create(MidiLooperPatch))
コード例 #22
0
 def setup_testcase(self):
     self.grid = pianoroll.PianoRollGrid()
     self.grid.setReadOnly(False)
     self.grid.setDuration(audioproc.MusicalDuration(8, 4))
     self.grid.resize(600, 400)
     self.setWidgetUnderTest(self.grid)
コード例 #23
0
ファイル: track_ui.py プロジェクト: wanderer6994/noisicaa
 def xToTime(self, x: int) -> audioproc.MusicalDuration:
     return audioproc.MusicalDuration(
         int(8 * self.measure.time_signature.upper * x / self.width()),
         8 * self.measure.time_signature.upper)
コード例 #24
0
 def durationPerPixel(self) -> audioproc.MusicalDuration:
     return audioproc.MusicalDuration(1 / self.scaleX())
コード例 #25
0
ファイル: model_test.py プロジェクト: wanderer6994/noisicaa
 async def test_duration(self):
     node = await self._add_node()
     self.assertEqual(node.duration, audioproc.MusicalDuration(8, 4))
     with self.project.apply_mutations('test'):
         node.set_duration(audioproc.MusicalDuration(4, 4))
     self.assertEqual(node.duration, audioproc.MusicalDuration(4, 4))
コード例 #26
0
    def paintForeground(self, painter: QtGui.QPainter) -> None:
        assert self._note_area is not None
        self._edit_areas.clear()

        ymid = self.height() // 2

        base_stave_line = self.measure.clef.center_pitch.stave_line
        base_octave = self.measure.clef.base_octave

        acc_map = {
            'C#': 'C%d' % (base_octave + 1),
            'D#': 'D%d' % (base_octave + 1),
            'E#': 'E%d' % (base_octave + 1),
            'F#': 'F%d' % (base_octave + 1),
            'G#': 'G%d' % (base_octave + 1),
            'A#': 'A%d' % base_octave,
            'B#': 'B%d' % base_octave,
            'Cb': 'C%d' % (base_octave + 1),
            'Db': 'D%d' % (base_octave + 1),
            'Eb': 'E%d' % (base_octave + 1),
            'Fb': 'F%d' % base_octave,
            'Gb': 'G%d' % base_octave,
            'Ab': 'A%d' % base_octave,
            'Bb': 'B%d' % base_octave,
        }

        active_accidentals = {}
        for acc in self.measure.key_signature.accidentals:
            value = acc_map[acc]
            active_accidentals[value[:1]] = acc[1:]

        x, note_area_width = self._note_area
        if note_area_width > 80:
            px = x - 20
            note_time = audioproc.MusicalDuration(0)
            for idx, note in enumerate(self.measure.notes):
                overflow = note_time + note.duration > self.measure.duration

                if note.is_rest:
                    sym = {
                        1: 'rest-whole',
                        2: 'rest-half',
                        4: 'rest-quarter',
                        8: 'rest-8th',
                        16: 'rest-16th',
                        32: 'rest-32th',
                    }[note.base_duration.denominator]
                    svg_symbol.paintSymbol(painter, sym,
                                           QtCore.QPoint(x, ymid))

                    if note.base_duration >= audioproc.MusicalDuration(1, 2):
                        dx = 25
                        dy = -10
                    else:
                        dx = 12
                        dy = 0

                    for d in range(note.dots):
                        painter.setPen(Qt.NoPen)
                        painter.setBrush(Qt.black)
                        painter.drawEllipse(dx - 4 + 10 * d, dy - 4, 9, 9)

                    if note.tuplet != 0:
                        painter.setPen(Qt.black)
                        painter.drawText(-5, -45, '%d' % note.tuplet)

                    # if overflow:
                    #     n.setOpacity(0.4)
                elif len(note.pitches) > 0:
                    min_stave_line = 1000
                    max_stave_line = -1000

                    for pitch in note.pitches:
                        stave_line = pitch.stave_line - base_stave_line
                        min_stave_line = min(min_stave_line, stave_line)
                        max_stave_line = max(max_stave_line, stave_line)

                    painter.setPen(Qt.black)
                    painter.setOpacity(0.4 if overflow else 0.8)

                    # Ledger lines above stave.
                    for l in range(6, max_stave_line + 1, 2):
                        painter.drawLine(x - 20, ymid - 10 * l, x + 20,
                                         ymid - 10 * l)

                    # Ledger lines below stave.
                    for l in range(-6, min_stave_line - 1, -2):
                        painter.drawLine(x - 20, ymid - 10 * l, x + 20,
                                         ymid - 10 * l)

                    painter.setOpacity(1.0)

                    for pitch in note.pitches:
                        stave_line = pitch.stave_line - base_stave_line

                        y = ymid - 10 * stave_line

                        active_accidental = active_accidentals.get(
                            pitch.value, '')
                        if pitch.accidental != active_accidental:
                            sym = self._accidental_map[pitch.accidental]
                            svg_symbol.paintSymbol(painter, sym,
                                                   QtCore.QPoint(x - 12, y))
                            active_accidentals[pitch.value] = pitch.accidental

                        if note.base_duration >= audioproc.MusicalDuration(
                                1, 2):
                            svg_symbol.paintSymbol(painter, 'note-head-void',
                                                   QtCore.QPoint(x, y))
                        else:
                            svg_symbol.paintSymbol(painter, 'note-head-black',
                                                   QtCore.QPoint(x, y))

                        if note.base_duration <= audioproc.MusicalDuration(
                                1, 2):
                            painter.fillRect(x + 8, y - 63, 3, 60, Qt.black)

                        if note.base_duration == audioproc.MusicalDuration(
                                1, 8):
                            flags = 1
                        elif note.base_duration == audioproc.MusicalDuration(
                                1, 16):
                            flags = 2
                        elif note.base_duration == audioproc.MusicalDuration(
                                1, 32):
                            flags = 3
                        else:
                            flags = 0

                        for f in range(flags):
                            svg_symbol.paintSymbol(
                                painter, 'note-flag-down',
                                QtCore.QPoint(x + 11, y - 63 + 12 * f))

                        for d in range(note.dots):
                            painter.setPen(Qt.NoPen)
                            painter.setBrush(Qt.black)
                            painter.drawEllipse(x + 12 + 10 * d, y - 4, 9, 9)

                        if note.tuplet != 0:
                            painter.drawText(x - 5, y - 85, '%d' % note.tuplet)

                    # if overflow:
                    #     n.setOpacity(0.4)

                x1 = max(x - 12, px)
                x2 = max(x + 13, x1)
                if x1 > px:
                    self._edit_areas.append((px, x1, idx, False))
                    px = x1
                if x2 > x1:
                    self._edit_areas.append((x1, x2, idx, True))
                    px = x2

                note_time += note.duration
                x += int(note_area_width * note.duration.fraction)

            if px < self.width():
                self._edit_areas.append(
                    (px, self.width(), len(self.measure.notes), False))
        else:
            self._note_area = (0, self.width())
コード例 #27
0
ファイル: base_track.py プロジェクト: wanderer6994/noisicaa
 def duration(self) -> audioproc.MusicalDuration:
     time_signature = self.get_property_value('time_signature')
     return audioproc.MusicalDuration(time_signature.upper,
                                      time_signature.lower)
コード例 #28
0
ファイル: base_track.py プロジェクト: wanderer6994/noisicaa
 def duration(self) -> audioproc.MusicalDuration:
     return audioproc.MusicalDuration(1, 1)
コード例 #29
0
    def paintGhost(self, painter: QtGui.QPainter) -> None:
        if self.__ghost_pos is None:
            return

        ymid = self.height() // 2

        tool = self.track_editor.currentToolType()
        pos = self.__ghost_pos
        painter.setOpacity(0.4)

        if tool.is_rest:
            sym = {
                1: 'rest-whole',
                2: 'rest-half',
                4: 'rest-quarter',
                8: 'rest-8th',
                16: 'rest-16th',
                32: 'rest-32th',
            }[self._tool_duration_map[tool].denominator]
            svg_symbol.paintSymbol(painter, sym, QtCore.QPoint(pos.x(), ymid))

        elif tool.is_note:
            duration = self._tool_duration_map[tool]

            if duration >= audioproc.MusicalDuration(1, 2):
                svg_symbol.paintSymbol(painter, 'note-head-void', pos)
            else:
                svg_symbol.paintSymbol(painter, 'note-head-black', pos)

            if duration <= audioproc.MusicalDuration(1, 2):
                painter.fillRect(pos.x() + 8, pos.y() - 63, 3, 60, Qt.black)

            if duration == audioproc.MusicalDuration(1, 8):
                flags = 1
            elif duration == audioproc.MusicalDuration(1, 16):
                flags = 2
            elif duration == audioproc.MusicalDuration(1, 32):
                flags = 3
            else:
                flags = 0

            for f in range(flags):
                svg_symbol.paintSymbol(
                    painter, 'note-flag-down',
                    QtCore.QPoint(pos.x() + 11,
                                  pos.y() - 63 + 12 * f))

        elif tool.is_accidental:
            accidental = {
                tools.ToolType.ACCIDENTAL_NATURAL: '',
                tools.ToolType.ACCIDENTAL_FLAT: 'b',
                tools.ToolType.ACCIDENTAL_SHARP: '#',
                tools.ToolType.ACCIDENTAL_DOUBLE_FLAT: 'bb',
                tools.ToolType.ACCIDENTAL_DOUBLE_SHARP: '##',
            }[tool]
            sym = self._accidental_map[accidental]
            svg_symbol.paintSymbol(painter, sym, pos)

        else:
            painter.setPen(Qt.NoPen)
            painter.setBrush(Qt.black)
            painter.drawEllipse(pos.x() - 15, pos.y() - 15, 31, 31)
コード例 #30
0
ファイル: base_track.py プロジェクト: wanderer6994/noisicaa
 def duration(self) -> audioproc.MusicalDuration:
     duration = audioproc.MusicalDuration()
     for mref in self.measure_list:
         duration += mref.measure.duration
     return duration