Permalink
Browse files

Merge changes for the 'command cleanup' sprint.

- Use a common Create/Update/Delete convention for naming commands.
- Simplify the API of the Command class.
- Add factory functions for the remaining commands in noisicaa.music.
- Rename node related stuff:
  - 'pipeline graph node' -> 'node'
  - 'pipeline graph connection' -> 'node connection'
  - 'pipeline graph' -> 'graph'
  • Loading branch information...
odahoda committed Jan 27, 2019
1 parent db14b7a commit 79b35d9689eccd260c854d32f1ebcc9ec2473603
Showing with 2,445 additions and 2,240 deletions.
  1. +34 −49 noisicaa/builtin_nodes/beat_track/client_impl_test.py
  2. +11 −7 noisicaa/builtin_nodes/beat_track/commands.proto
  3. +31 −20 noisicaa/builtin_nodes/beat_track/commands.py
  4. +74 −3 noisicaa/builtin_nodes/beat_track/node_ui.py
  5. +40 −42 noisicaa/builtin_nodes/beat_track/server_impl.py
  6. +6 −8 noisicaa/builtin_nodes/beat_track/track_ui.py
  7. +15 −19 noisicaa/builtin_nodes/commands_registry.proto
  8. +33 −20 noisicaa/builtin_nodes/control_track/client_impl_test.py
  9. +10 −9 noisicaa/builtin_nodes/control_track/commands.proto
  10. +26 −18 noisicaa/builtin_nodes/control_track/commands.py
  11. +1 −1 noisicaa/builtin_nodes/control_track/node_ui.py
  12. +30 −43 noisicaa/builtin_nodes/control_track/server_impl.py
  13. +1 −1 noisicaa/builtin_nodes/control_track/server_impl_test.py
  14. +15 −14 noisicaa/builtin_nodes/control_track/track_ui.py
  15. +1 −1 noisicaa/builtin_nodes/custom_csound/client_impl.py
  16. +4 −7 noisicaa/builtin_nodes/custom_csound/client_impl_test.py
  17. +3 −2 noisicaa/builtin_nodes/custom_csound/commands.proto
  18. +12 −6 noisicaa/builtin_nodes/custom_csound/commands.py
  19. +1 −1 noisicaa/builtin_nodes/custom_csound/model.py
  20. +5 −5 noisicaa/builtin_nodes/custom_csound/node_ui.py
  21. +9 −14 noisicaa/builtin_nodes/custom_csound/server_impl.py
  22. +1 −1 noisicaa/builtin_nodes/custom_csound/server_impl_test.py
  23. +1 −1 noisicaa/builtin_nodes/instrument/client_impl.py
  24. +4 −7 noisicaa/builtin_nodes/instrument/client_impl_test.py
  25. +2 −1 noisicaa/builtin_nodes/instrument/commands.proto
  26. +8 −4 noisicaa/builtin_nodes/instrument/commands.py
  27. +1 −1 noisicaa/builtin_nodes/instrument/model.py
  28. +4 −4 noisicaa/builtin_nodes/instrument/node_ui.py
  29. +7 −12 noisicaa/builtin_nodes/instrument/server_impl.py
  30. +1 −1 noisicaa/builtin_nodes/instrument/server_impl_test.py
  31. +1 −1 noisicaa/builtin_nodes/midi_source/client_impl.py
  32. +6 −9 noisicaa/builtin_nodes/midi_source/client_impl_test.py
  33. +3 −2 noisicaa/builtin_nodes/midi_source/commands.proto
  34. +12 −6 noisicaa/builtin_nodes/midi_source/commands.py
  35. +1 −1 noisicaa/builtin_nodes/midi_source/model.py
  36. +4 −4 noisicaa/builtin_nodes/midi_source/node_ui.py
  37. +9 −14 noisicaa/builtin_nodes/midi_source/server_impl.py
  38. +1 −1 noisicaa/builtin_nodes/midi_source/server_impl_test.py
  39. +3 −3 noisicaa/builtin_nodes/mixer/node_ui.py
  40. +17 −19 noisicaa/builtin_nodes/sample_track/client_impl_test.py
  41. +11 −9 noisicaa/builtin_nodes/sample_track/commands.proto
  42. +23 −18 noisicaa/builtin_nodes/sample_track/commands.py
  43. +1 −1 noisicaa/builtin_nodes/sample_track/node_ui.py
  44. +28 −40 noisicaa/builtin_nodes/sample_track/server_impl.py
  45. +1 −1 noisicaa/builtin_nodes/sample_track/server_impl_test.py
  46. +12 −10 noisicaa/builtin_nodes/sample_track/track_ui.py
  47. +94 −132 noisicaa/builtin_nodes/score_track/client_impl_test.py
  48. +28 −39 noisicaa/builtin_nodes/score_track/commands.proto
  49. +67 −80 noisicaa/builtin_nodes/score_track/commands.py
  50. +6 −7 noisicaa/builtin_nodes/score_track/node_ui.py
  51. +70 −134 noisicaa/builtin_nodes/score_track/server_impl.py
  52. +1 −1 noisicaa/builtin_nodes/score_track/server_impl_test.py
  53. +78 −113 noisicaa/builtin_nodes/score_track/track_ui.py
  54. +52 −29 noisicaa/builtin_nodes/server_registry.py
  55. +1 −1 noisicaa/builtin_nodes/ui_registry.py
  56. +8 −6 noisicaa/core/ipc.py
  57. +1 −0 noisicaa/model/CMakeLists.txt
  58. +6 −9 noisicaa/model/__init__.py
  59. +70 −0 noisicaa/model/control_value.py
  60. +45 −5 noisicaa/model/model_base.py
  61. +17 −4 noisicaa/model/model_base_test.py
  62. +15 −20 noisicaa/model/project.proto
  63. +60 −114 noisicaa/model/project.py
  64. +2 −2 noisicaa/music/CMakeLists.txt
  65. +16 −24 noisicaa/music/__init__.py
  66. +78 −38 noisicaa/music/base_track.py
  67. +6 −16 noisicaa/music/base_track_test.py
  68. +69 −84 noisicaa/music/commands.proto
  69. +129 −30 noisicaa/music/commands.py
  70. +2 −2 noisicaa/music/commands_test.py
  71. +116 −82 noisicaa/music/{pipeline_graph.py → graph.py}
  72. +163 −0 noisicaa/music/graph_test.py
  73. +9 −0 noisicaa/music/mutations.proto
  74. +31 −2 noisicaa/music/mutations.py
  75. +1 −1 noisicaa/music/node_connector.py
  76. +0 −174 noisicaa/music/pipeline_graph_test.py
  77. +4 −4 noisicaa/music/player.py
  78. +1 −1 noisicaa/music/player_test.py
  79. +29 −40 noisicaa/music/pmodel.py
  80. +24 −42 noisicaa/music/pmodel_test.py
  81. +132 −239 noisicaa/music/project.py
  82. +169 −8 noisicaa/music/project_client.py
  83. +11 −16 noisicaa/music/project_client_model.py
  84. +10 −8 noisicaa/music/project_client_test.py
  85. +4 −5 noisicaa/music/project_integration_test.py
  86. +35 −22 noisicaa/music/project_process.py
  87. +40 −17 noisicaa/music/project_test.py
  88. +1 −1 noisicaa/ui/CMakeLists.txt
  89. +6 −7 noisicaa/ui/control_value_connector.py
  90. 0 noisicaa/ui/{pipeline_graph → graph}/CMakeLists.txt
  91. +1 −1 noisicaa/ui/{pipeline_graph → graph}/__init__.py
  92. +14 −19 noisicaa/ui/{pipeline_graph → graph}/base_node.py
  93. +4 −7 noisicaa/ui/{pipeline_graph → graph}/base_node_test.py
  94. +40 −53 noisicaa/ui/{pipeline_graph → graph}/canvas.py
  95. 0 noisicaa/ui/{pipeline_graph → graph}/canvas_test.py
  96. +6 −6 noisicaa/ui/{pipeline_graph → graph}/generic_node.py
  97. +2 −2 noisicaa/ui/{pipeline_graph → graph}/plugin_node.py
  98. 0 noisicaa/ui/{pipeline_graph → graph}/toolbox.py
  99. +4 −6 noisicaa/ui/{pipeline_graph → graph}/track_node.py
  100. +1 −1 noisicaa/ui/{pipeline_graph → graph}/view.py
  101. +2 −2 noisicaa/ui/{pipeline_graph → graph}/view_test.py
  102. +7 −12 noisicaa/ui/project_view.py
  103. +17 −22 noisicaa/ui/track_list/editor.py
  104. +45 −25 noisicaa/ui/track_list/measured_track_editor.py
  105. +12 −3 noisicaa/ui/ui_base.py
  106. +59 −61 noisidev/demo_project.py
@@ -23,8 +23,8 @@
from noisidev import unittest
from noisicaa import audioproc
from noisicaa import model
from noisicaa import music
from noisicaa.music import base_track_test
from noisicaa.music import commands_pb2
from . import client_impl
from . import commands

@@ -33,92 +33,77 @@ class BeatTrackTest(base_track_test.TrackTestMixin, unittest.AsyncTestCase):
node_uri = 'builtin://beat-track'
track_cls = client_impl.BeatTrack

async def test_insert_measure(self):
async def test_create_measure(self):
track = await self._add_track()
self.assertEqual(len(track.measure_list), 1)

await self.client.send_command(commands_pb2.Command(
target=track.id,
command='insert_measure',
insert_measure=commands_pb2.InsertMeasure(
pos=0,
tracks=[track.id])))
await self.client.send_command(music.create_measure(
track, pos=0))
self.assertEqual(len(track.measure_list), 2)

async def test_append_measure(self):
async def test_delete_measure(self):
track = await self._add_track()
self.assertEqual(len(track.measure_list), 1)

await self.client.send_command(commands_pb2.Command(
target=track.id,
command='insert_measure',
insert_measure=commands_pb2.InsertMeasure(
tracks=[])))
self.assertEqual(len(track.measure_list), 2)

async def test_remove_measure(self):
track = await self._add_track()
self.assertEqual(len(track.measure_list), 1)

await self.client.send_command(commands_pb2.Command(
target=track.id,
command='remove_measure',
remove_measure=commands_pb2.RemoveMeasure(
pos=0,
tracks=[])))
await self.client.send_command(music.delete_measure(
track.measure_list[0]))
self.assertEqual(len(track.measure_list), 0)

async def test_clear_measures(self):
track = await self._add_track()
self.assertEqual(len(track.measure_list), 1)
old_measure = track.measure_list[0].measure

await self.client.send_command(commands_pb2.Command(
target=self.project.id,
command='clear_measures',
clear_measures=commands_pb2.ClearMeasures(
measure_ids=[track.measure_list[0].id])))
await self.client.send_command(music.update_measure(
measure=track.measure_list[0],
clear=True))
self.assertIsNot(old_measure, track.measure_list[0].measure)

async def test_set_beat_track_pitch(self):
async def test_set_pitch(self):
track = await self._add_track()

await self.client.send_command(commands.set_beat_track_pitch(
track.id,
pitch=model.Pitch('C2')))
await self.client.send_command(commands.update(
track,
set_pitch=model.Pitch('C2')))
self.assertEqual(track.pitch, model.Pitch('C2'))

async def test_add_beat(self):
track = await self._add_track()
measure = track.measure_list[0].measure

await self.client.send_command(commands.add_beat(
measure.id,
await self.client.send_command(commands.create_beat(
measure,
time=audioproc.MusicalDuration(1, 4)))
self.assertEqual(measure.beats[0].time, audioproc.MusicalDuration(1, 4))
self.assertEqual(measure.beats[0].velocity, 100)

async def test_remove_beat(self):
await self.client.send_command(commands.create_beat(
measure,
time=audioproc.MusicalDuration(2, 4),
velocity=120))
self.assertEqual(measure.beats[1].time, audioproc.MusicalDuration(2, 4))
self.assertEqual(measure.beats[1].velocity, 120)

async def test_delete_beat(self):
track = await self._add_track()
measure = track.measure_list[0].measure
await self.client.send_command(commands.add_beat(
measure.id,
await self.client.send_command(commands.create_beat(
measure,
time=audioproc.MusicalDuration(1, 4)))

await self.client.send_command(commands.remove_beat(
measure.id,
beat_id=measure.beats[0].id))
await self.client.send_command(commands.delete_beat(
measure.beats[0]))
self.assertEqual(len(measure.beats), 0)

async def test_set_beat_velocity(self):
async def test_beat_set_velocity(self):
track = await self._add_track()
measure = track.measure_list[0].measure
await self.client.send_command(commands.add_beat(
measure.id,
await self.client.send_command(commands.create_beat(
measure,
time=audioproc.MusicalDuration(1, 4)))
beat = measure.beats[0]

await self.client.send_command(commands.set_beat_velocity(
beat.id,
velocity=57))
await self.client.send_command(commands.update_beat(
beat,
set_velocity=57))
self.assertEqual(beat.velocity, 57)
@@ -27,18 +27,22 @@ import "noisicaa/model/project.proto";

package noisicaa.pb;

message SetBeatTrackPitch {
optional Pitch pitch = 1;
message UpdateBeatTrack {
required uint64 track_id = 1;
optional Pitch set_pitch = 2;
}

message SetBeatVelocity {
optional uint32 velocity = 1;
message CreateBeat {
required uint64 measure_id = 1;
optional MusicalDuration time = 2;
optional uint32 velocity = 3;
}

message AddBeat {
optional MusicalDuration time = 1;
message UpdateBeat {
required uint64 beat_id = 1;
optional uint32 set_velocity = 2;
}

message RemoveBeat {
message DeleteBeat {
optional uint64 beat_id = 1;
}
@@ -24,31 +24,42 @@
from noisicaa import model
from noisicaa import music
from noisicaa.builtin_nodes import commands_registry_pb2
from . import client_impl

def set_beat_track_pitch(
node_id: int, *, pitch: model.Pitch) -> music.Command:
cmd = music.Command(target=node_id, command='set_beat_track_pitch')
pb = cmd.Extensions[commands_registry_pb2.set_beat_track_pitch]
pb.pitch.CopyFrom(pitch.to_proto())
return cmd

def set_beat_velocity(
node_id: int, *, velocity: int) -> music.Command:
cmd = music.Command(target=node_id, command='set_beat_velocity')
pb = cmd.Extensions[commands_registry_pb2.set_beat_velocity]
pb.velocity = velocity
def update(
track: client_impl.BeatTrack, *, set_pitch: model.Pitch = None) -> music.Command:
cmd = music.Command(command='update_beat_track')
pb = cmd.Extensions[commands_registry_pb2.update_beat_track]
pb.track_id = track.id
if set_pitch is not None:
pb.set_pitch.CopyFrom(set_pitch.to_proto())
return cmd

def add_beat(
node_id: int, *, time: audioproc.MusicalTime) -> music.Command:
cmd = music.Command(target=node_id, command='add_beat')
pb = cmd.Extensions[commands_registry_pb2.add_beat]
def create_beat(
measure: client_impl.BeatMeasure, *,
time: audioproc.MusicalTime,
velocity: int = None
) -> music.Command:
cmd = music.Command(command='create_beat')
pb = cmd.Extensions[commands_registry_pb2.create_beat]
pb.measure_id = measure.id
pb.time.CopyFrom(time.to_proto())
if velocity is not None:
pb.velocity = velocity
return cmd

def update_beat(
beat: client_impl.Beat, *, set_velocity: int = None) -> music.Command:
cmd = music.Command(command='update_beat')
pb = cmd.Extensions[commands_registry_pb2.update_beat]
pb.beat_id = beat.id
if set_velocity is not None:
pb.set_velocity = set_velocity
return cmd

def remove_beat(
node_id: int, *, beat_id: int) -> music.Command:
cmd = music.Command(target=node_id, command='remove_beat')
pb = cmd.Extensions[commands_registry_pb2.remove_beat]
pb.beat_id = beat_id
def delete_beat(beat: client_impl.Beat) -> music.Command:
cmd = music.Command(command='delete_beat')
pb = cmd.Extensions[commands_registry_pb2.delete_beat]
pb.beat_id = beat.id
return cmd
@@ -20,20 +20,91 @@
#
# @end:license

from typing import Any
from typing import Any, Dict
import logging
import os.path

from PyQt5.QtCore import Qt
from PyQt5 import QtWidgets
from PyQt5 import QtSvg

from noisicaa.constants import DATA_DIR
from noisicaa.ui.pipeline_graph import track_node
from noisicaa import core
from noisicaa import model
from noisicaa import music
from noisicaa.ui.graph import track_node
from noisicaa.ui import ui_base
from . import commands
from . import client_impl

logger = logging.getLogger(__name__)


class BeatTrackWidget(ui_base.ProjectMixin, QtWidgets.QScrollArea):
def __init__(self, track: client_impl.BeatTrack, **kwargs: Any) -> None:
super().__init__(**kwargs)

self.__track = track

self.__listeners = {} # type: Dict[str, core.Listener]

body = QtWidgets.QWidget(self)
body.setAutoFillBackground(False)
body.setAttribute(Qt.WA_NoSystemBackground, True)

self.__pitch = QtWidgets.QLineEdit(body)
self.__pitch.editingFinished.connect(self.__pitchEdited)
self.__pitch.setText(str(self.__track.pitch))
self.__listeners['track:pitch'] = (
self.__track.pitch_changed.add(self.__pitchChanged))

layout = QtWidgets.QFormLayout()
layout.setVerticalSpacing(1)
layout.setContentsMargins(0, 0, 0, 0)
layout.addRow("Pitch:", self.__pitch)
body.setLayout(layout)

self.setWidgetResizable(True)
self.setFrameShape(QtWidgets.QFrame.NoFrame)
self.setWidget(body)

def cleanup(self) -> None:
for listener in self.__listeners.values():
listener.remove()
self.__listeners.clear()

def __pitchChanged(self, change: model.PropertyValueChange[str]) -> None:
self.__pitch.setText(str(change.new_value))

def __pitchEdited(self) -> None:
try:
pitch = model.Pitch(self.__pitch.text())
except ValueError:
self.__pitch.setText(str(self.__track.pitch))
else:
if pitch != self.__track.pitch:
self.send_command_async(commands.update(
self.__track,
set_pitch=pitch))


class BeatTrackNode(track_node.TrackNode):
def __init__(self, **kwargs: Any) -> None:
def __init__(self, node: music.BaseNode, **kwargs: Any) -> None:
assert isinstance(node, client_impl.BeatTrack)
self.__widget = None # type: BeatTrackWidget
self.__track = node # type: client_impl.BeatTrack

super().__init__(
node=node,
icon=QtSvg.QSvgRenderer(os.path.join(DATA_DIR, 'icons', 'track-type-beat.svg')),
**kwargs)

def cleanup(self) -> None:
if self.__widget is not None:
self.__widget.cleanup()
super().cleanup()

def createBodyWidget(self) -> QtWidgets.QWidget:
assert self.__widget is None
self.__widget = BeatTrackWidget(track=self.__track, context=self.context)
return self.__widget
Oops, something went wrong.

0 comments on commit 79b35d9

Please sign in to comment.