add midi function

This commit is contained in:
flandre 2025-04-14 01:34:05 +08:00
parent 5fb6af0aa4
commit 3c559bd689
8 changed files with 154 additions and 40 deletions

View File

@ -17,6 +17,7 @@ from flandre.nodes.Muxer import Muxer
from flandre.nodes.Robot import Robot from flandre.nodes.Robot import Robot
from flandre.nodes.ImageFFMPEG import ImageFFMPEG from flandre.nodes.ImageFFMPEG import ImageFFMPEG
from flandre.nodes.ImageQt import ImageQt from flandre.nodes.ImageQt import ImageQt
from flandre.nodes.Midi import Midi
from flandre.utils.Msg import KillMsg from flandre.utils.Msg import KillMsg
from flandre.config import CONFIG_FOLDER from flandre.config import CONFIG_FOLDER
@ -31,6 +32,7 @@ class LaunchComponent(Enum):
Beamformer = Beamformer Beamformer = Beamformer
ImageFFMPEG = ImageFFMPEG ImageFFMPEG = ImageFFMPEG
ImageQt = ImageQt ImageQt = ImageQt
Midi = Midi
def launch(arg: dict[LaunchComponent, dict]): def launch(arg: dict[LaunchComponent, dict]):

View File

@ -40,7 +40,7 @@ class Beamformer(Node):
.call(cp.asarray, order='C') .call(cp.asarray, order='C')
.argrelextrema() .argrelextrema()
.conv_guass(b=arg.beta * 0.01) .conv_guass(b=arg.beta * 0.01)
.crop(arg.t_start, arg.t_end) .crop_center(arg.t_start, arg.t_end)
.rotate90() .rotate90()
.cpu() .cpu()
) )

View File

@ -345,12 +345,25 @@ class Adv(QMainWindow, Ui_MainWindow):
elif isinstance(msg, ImageArgMsg): elif isinstance(msg, ImageArgMsg):
self.arg = msg self.arg = msg
self.arg.sender = 'ui' self.arg.sender = 'ui'
self.s_t_start.setValue(msg.t_start) self.s_t_start.setValue(msg.t_start)
self.s_t_end.setValue(msg.t_end) self.s_t_end.setValue(msg.t_end)
self.s_f_rows.setValue(msg.f_rows) self.s_f_rows.setValue(msg.f_rows)
self.s_v2.setValue(msg.v2) self.s_v2.setValue(msg.v2)
self.s_dct_center.setValue(msg.dct_center) self.s_dct_center.setValue(msg.dct_center)
self.s_dct_bandwidth.setValue(msg.dct_bandwidth) self.s_dct_bandwidth.setValue(msg.dct_bandwidth)
self.s_beta.setValue(msg.beta)
self.sp_crop_center.setValue(msg.t_start)
self.sp_crop_width.setValue(msg.t_end)
self.sp_f_rows.setValue(msg.f_rows)
self.sp_v2.setValue(msg.v2)
self.sp_dct_center.setValue(msg.dct_center)
self.sp_dct_bandwidth.setValue(msg.dct_bandwidth)
self.sp_beta.setValue(msg.beta)
elif isinstance(msg, MoveAxisMsg): elif isinstance(msg, MoveAxisMsg):
match msg.axis: match msg.axis:
case 'S': case 'S':
@ -376,11 +389,19 @@ class Adv(QMainWindow, Ui_MainWindow):
elif isinstance(msg, SetSeqMetaMsg): elif isinstance(msg, SetSeqMetaMsg):
self.seq_meta = RfSequenceMeta.from_name(msg.name) self.seq_meta = RfSequenceMeta.from_name(msg.name)
mmax_shape0 = max(self.seq_meta.shape) mmax_shape0 = max(self.seq_meta.shape)
self.s_t_start.setMaximum(mmax_shape0) self.s_t_start.setMaximum(mmax_shape0)
self.s_t_end.setMaximum(mmax_shape0) self.s_t_end.setMaximum(mmax_shape0)
self.s_dct_center.setMaximum(mmax_shape0) self.s_dct_center.setMaximum(mmax_shape0)
self.s_dct_bandwidth.setMaximum(mmax_shape0) self.s_dct_bandwidth.setMaximum(mmax_shape0)
self.s_f_rows.setMaximum(mmax_shape0) self.s_f_rows.setMaximum(mmax_shape0)
self.sp_crop_center.setMaximum(mmax_shape0)
self.sp_crop_width.setMaximum(mmax_shape0)
self.sp_dct_center.setMaximum(mmax_shape0)
self.sp_dct_bandwidth.setMaximum(mmax_shape0)
self.sp_f_rows.setMaximum(mmax_shape0)
elif isinstance(msg, DeviceConnectedMsg): elif isinstance(msg, DeviceConnectedMsg):
if msg.value: if msg.value:
self.set_device_connection(LinkStatus.GREEN) self.set_device_connection(LinkStatus.GREEN)
@ -456,6 +477,24 @@ class Adv(QMainWindow, Ui_MainWindow):
if self.cb_bscan.sender() is None: if self.cb_bscan.sender() is None:
self.p.send(SetWindowVisibleMsg('ui', 'bscan', v == 2)) self.p.send(SetWindowVisibleMsg('ui', 'bscan', v == 2))
@pyqtSlot(int)
def on_sp_crop_center_valueChanged(self, v):
if self.sp_crop_center.sender() is None:
self.arg.t_start = v
self.p.send(self.arg)
@pyqtSlot(int)
def on_sp_crop_width_valueChanged(self, v):
if self.sp_crop_width.sender() is None:
self.arg.t_end = v
self.p.send(self.arg)
@pyqtSlot(int)
def on_sp_v2_valueChanged(self, v):
if self.sp_v2.sender() is None:
self.arg.v2 = v
self.p.send(self.arg)
@pyqtSlot(int) @pyqtSlot(int)
def on_s_beta_valueChanged(self, v): def on_s_beta_valueChanged(self, v):
if self.s_beta.sender() is None: if self.s_beta.sender() is None:

View File

@ -1,5 +1,6 @@
import logging import logging
from threading import Thread from threading import Thread
from unittest import case
import mido import mido
import zmq import zmq
@ -7,12 +8,15 @@ from mido import Message
from mido.backends.rtmidi import Input, Output from mido.backends.rtmidi import Input, Output
from flandre.nodes.Node import Node from flandre.nodes.Node import Node
from flandre.utils.Msg import KillMsg, MidiMsg, Msg, ImageArgMsg from flandre.utils.Msg import KillMsg, MidiMsg, Msg, ImageArgMsg, SetSeqMetaMsg, SetSidMsg
from flandre.utils.RfMeta import RfSequenceMeta
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Midi(Node): class Midi(Node):
topics = [ImageArgMsg, SetSeqMetaMsg]
def __init__(self, level=logging.INFO): def __init__(self, level=logging.INFO):
super(Midi, self).__init__(level=level) super(Midi, self).__init__(level=level)
self.m_input: Input = None self.m_input: Input = None
@ -20,6 +24,15 @@ class Midi(Node):
self.do_loop = True self.do_loop = True
self.t_midi_event_loop: Thread = None self.t_midi_event_loop: Thread = None
self.isa: zmq.Socket = None self.isa: zmq.Socket = None
self.arg = ImageArgMsg('midi', 0)
self.m_t_start = 100
self.m_t_end = 100
self.m_dct_center = 100
self.m_dct_bandwidth = 100
self.m_f_rows = 100
self.last_pitch: dict[int, int] = dict()
self.sid = 0
def custom_setup(self): def custom_setup(self):
self.isa = self.c.ctx.socket(zmq.PUSH) self.isa = self.c.ctx.socket(zmq.PUSH)
@ -33,16 +46,32 @@ class Midi(Node):
def midi_event_loop(self): def midi_event_loop(self):
while self.do_loop: while self.do_loop:
midi_msg: Message = self.m_input.receive() midi_msg: Message = self.m_input.receive()
# print(midi_msg) print(midi_msg)
d = midi_msg.dict() d = midi_msg.dict()
match d['type']: match d['type']:
case 'pitchwheel': case 'pitchwheel':
channel = d['channel'] channel = d['channel']
# 0-127 # 0-127
pitch = int(d['pitch'] / 128) + 64 pitch = int(d['pitch'] / 128) + 64
if channel not in self.last_pitch:
self.last_pitch[channel] = pitch
elif abs(pitch - self.last_pitch[channel]) > 5:
pass
else:
self.last_pitch[channel] = pitch
# pitch_p = int(pitch / 127) # pitch_p = int(pitch / 127)
self.isa.send(MidiMsg(type='pitchwheel', channel=channel, pitch=pitch).encode_msg()) self.isa.send(MidiMsg(type='pitchwheel', channel=channel, pitch=pitch).encode_msg())
case 'control_change':
for i in range(16, 24):
if i in [d['control'], d['value']]:
if 1 in [d['control'], d['value']]:
self.isa.send(MidiMsg(type='control_change', value=1, control=i - 16).encode_msg())
else:
self.isa.send(MidiMsg(type='control_change', value=-1, control=i - 16).encode_msg())
case 'note_on':
self.isa.send(MidiMsg(type='note_on', note=d['note'], velocity=d['velocity']).encode_msg())
case 'note_off':
self.isa.send(MidiMsg(type='note_off', note=d['note'], velocity=d['velocity']).encode_msg())
def loop(self): def loop(self):
isb = self.c.ctx.socket(zmq.PULL) isb = self.c.ctx.socket(zmq.PULL)
isb.connect("inproc://midi") isb.connect("inproc://midi")
@ -50,16 +79,42 @@ class Midi(Node):
while True: while True:
p = dict(self.c.poller.poll()) p = dict(self.c.poller.poll())
if isb in p: if isb in p:
msg = Msg.decode_msg(isb.recv()) msg: MidiMsg = Msg.decode_msg(isb.recv())
match msg.type: match msg.type:
case 'pitchwheel': case 'pitchwheel':
match msg.channel: match msg.channel:
case 0: case 0:
self.arg.v2 = int(100 + 6000 * (msg.pitch / 127))
# print(msg.pitch) # print(msg.pitch)
self.send(ImageArgMsg('midi', 0, msg.pitch * 3)) self.send(self.arg)
case 'control_change':
match msg.control:
case 0:
self.arg.t_start = sorted((1, self.arg.t_start + msg.value * 10, self.m_t_start))[1]
case 1:
self.arg.t_end = sorted((1, self.arg.t_end + msg.value * 10, self.m_t_end))[1]
case 2:
self.arg.v2 = sorted((500, self.arg.v2 + msg.value * 10, 7000))[1]
case 'note_on':
self.sid += 1
self.send(SetSidMsg(self.sid))
self.send(self.arg)
if self.c.sub in p: if self.c.sub in p:
msg = self.recv() msg = self.recv()
if isinstance(msg, KillMsg): if isinstance(msg, KillMsg):
if msg.name == '': if msg.name == '':
self.do_loop = False self.do_loop = False
return return
elif isinstance(msg, ImageArgMsg):
if msg.sender != 'midi':
self.arg = msg
self.arg.sender = 'midi'
elif isinstance(msg, SetSeqMetaMsg):
seq_meta = RfSequenceMeta.from_name(msg.name)
mmax_shape0 = max(seq_meta.shape)
self.m_t_start = mmax_shape0
self.m_t_end = mmax_shape0
self.m_dct_center = mmax_shape0
self.m_dct_bandwidth = mmax_shape0
self.m_f_rows = mmax_shape0

View File

@ -485,9 +485,9 @@ class Ui_MainWindow(object):
self.label_7 = QtWidgets.QLabel(parent=self.centralwidget) self.label_7 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_7.setObjectName("label_7") self.label_7.setObjectName("label_7")
self.gridLayout_5.addWidget(self.label_7, 6, 0, 1, 1) self.gridLayout_5.addWidget(self.label_7, 6, 0, 1, 1)
self.spinBox = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_crop_width = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox.setObjectName("spinBox") self.sp_crop_width.setObjectName("sp_crop_width")
self.gridLayout_5.addWidget(self.spinBox, 4, 2, 1, 1) self.gridLayout_5.addWidget(self.sp_crop_width, 4, 2, 1, 1)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding) spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
self.gridLayout_5.addItem(spacerItem2, 10, 1, 1, 1) self.gridLayout_5.addItem(spacerItem2, 10, 1, 1, 1)
self.s_t_end = QJumpSlider(parent=self.centralwidget) self.s_t_end = QJumpSlider(parent=self.centralwidget)
@ -496,9 +496,9 @@ class Ui_MainWindow(object):
self.s_t_end.setOrientation(QtCore.Qt.Orientation.Horizontal) self.s_t_end.setOrientation(QtCore.Qt.Orientation.Horizontal)
self.s_t_end.setObjectName("s_t_end") self.s_t_end.setObjectName("s_t_end")
self.gridLayout_5.addWidget(self.s_t_end, 4, 1, 1, 1) self.gridLayout_5.addWidget(self.s_t_end, 4, 1, 1, 1)
self.spinBox_7 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_dct_bandwidth = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_7.setObjectName("spinBox_7") self.sp_dct_bandwidth.setObjectName("sp_dct_bandwidth")
self.gridLayout_5.addWidget(self.spinBox_7, 7, 2, 1, 1) self.gridLayout_5.addWidget(self.sp_dct_bandwidth, 7, 2, 1, 1)
self.label_5 = QtWidgets.QLabel(parent=self.centralwidget) self.label_5 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_5.setObjectName("label_5") self.label_5.setObjectName("label_5")
self.gridLayout_5.addWidget(self.label_5, 4, 0, 1, 1) self.gridLayout_5.addWidget(self.label_5, 4, 0, 1, 1)
@ -518,15 +518,17 @@ class Ui_MainWindow(object):
self.label_6 = QtWidgets.QLabel(parent=self.centralwidget) self.label_6 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_6.setObjectName("label_6") self.label_6.setObjectName("label_6")
self.gridLayout_5.addWidget(self.label_6, 5, 0, 1, 1) self.gridLayout_5.addWidget(self.label_6, 5, 0, 1, 1)
self.spinBox_2 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_v2 = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_2.setObjectName("spinBox_2") self.sp_v2.setMinimum(500)
self.gridLayout_5.addWidget(self.spinBox_2, 5, 2, 1, 1) self.sp_v2.setMaximum(7000)
self.spinBox_3 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_v2.setObjectName("sp_v2")
self.spinBox_3.setObjectName("spinBox_3") self.gridLayout_5.addWidget(self.sp_v2, 5, 2, 1, 1)
self.gridLayout_5.addWidget(self.spinBox_3, 6, 2, 1, 1) self.sp_dct_center = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_13 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_dct_center.setObjectName("sp_dct_center")
self.spinBox_13.setObjectName("spinBox_13") self.gridLayout_5.addWidget(self.sp_dct_center, 6, 2, 1, 1)
self.gridLayout_5.addWidget(self.spinBox_13, 8, 2, 1, 1) self.sp_f_rows = QtWidgets.QSpinBox(parent=self.centralwidget)
self.sp_f_rows.setObjectName("sp_f_rows")
self.gridLayout_5.addWidget(self.sp_f_rows, 8, 2, 1, 1)
self.s_f_rows = QtWidgets.QSlider(parent=self.centralwidget) self.s_f_rows = QtWidgets.QSlider(parent=self.centralwidget)
self.s_f_rows.setMinimum(500) self.s_f_rows.setMinimum(500)
self.s_f_rows.setMaximum(7000) self.s_f_rows.setMaximum(7000)
@ -538,7 +540,7 @@ class Ui_MainWindow(object):
self.s_dct_bandwidth.setObjectName("s_dct_bandwidth") self.s_dct_bandwidth.setObjectName("s_dct_bandwidth")
self.gridLayout_5.addWidget(self.s_dct_bandwidth, 7, 1, 1, 1) self.gridLayout_5.addWidget(self.s_dct_bandwidth, 7, 1, 1, 1)
self.s_v2 = QtWidgets.QSlider(parent=self.centralwidget) self.s_v2 = QtWidgets.QSlider(parent=self.centralwidget)
self.s_v2.setMinimum(1) self.s_v2.setMinimum(500)
self.s_v2.setMaximum(7000) self.s_v2.setMaximum(7000)
self.s_v2.setProperty("value", 5900) self.s_v2.setProperty("value", 5900)
self.s_v2.setOrientation(QtCore.Qt.Orientation.Horizontal) self.s_v2.setOrientation(QtCore.Qt.Orientation.Horizontal)
@ -556,9 +558,9 @@ class Ui_MainWindow(object):
self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.label_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.label_2.setObjectName("label_2") self.label_2.setObjectName("label_2")
self.gridLayout_5.addWidget(self.label_2, 0, 0, 1, 3) self.gridLayout_5.addWidget(self.label_2, 0, 0, 1, 3)
self.spinBox_12 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_crop_center = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_12.setObjectName("spinBox_12") self.sp_crop_center.setObjectName("sp_crop_center")
self.gridLayout_5.addWidget(self.spinBox_12, 3, 2, 1, 1) self.gridLayout_5.addWidget(self.sp_crop_center, 3, 2, 1, 1)
self.s_beta = QtWidgets.QSlider(parent=self.centralwidget) self.s_beta = QtWidgets.QSlider(parent=self.centralwidget)
self.s_beta.setMinimum(1) self.s_beta.setMinimum(1)
self.s_beta.setMaximum(30) self.s_beta.setMaximum(30)
@ -569,9 +571,9 @@ class Ui_MainWindow(object):
self.label_35 = QtWidgets.QLabel(parent=self.centralwidget) self.label_35 = QtWidgets.QLabel(parent=self.centralwidget)
self.label_35.setObjectName("label_35") self.label_35.setObjectName("label_35")
self.gridLayout_5.addWidget(self.label_35, 9, 0, 1, 1) self.gridLayout_5.addWidget(self.label_35, 9, 0, 1, 1)
self.spinBox_14 = QtWidgets.QSpinBox(parent=self.centralwidget) self.sp_beta = QtWidgets.QSpinBox(parent=self.centralwidget)
self.spinBox_14.setObjectName("spinBox_14") self.sp_beta.setObjectName("sp_beta")
self.gridLayout_5.addWidget(self.spinBox_14, 9, 2, 1, 1) self.gridLayout_5.addWidget(self.sp_beta, 9, 2, 1, 1)
self.gridLayout.addLayout(self.gridLayout_5, 3, 0, 1, 1) self.gridLayout.addLayout(self.gridLayout_5, 3, 0, 1, 1)
self.gridLayout.setRowStretch(0, 1) self.gridLayout.setRowStretch(0, 1)
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)

View File

@ -910,7 +910,7 @@ border-radius: 7px;
</widget> </widget>
</item> </item>
<item row="4" column="2"> <item row="4" column="2">
<widget class="QSpinBox" name="spinBox"/> <widget class="QSpinBox" name="sp_crop_width"/>
</item> </item>
<item row="10" column="1"> <item row="10" column="1">
<spacer name="verticalSpacer_2"> <spacer name="verticalSpacer_2">
@ -939,7 +939,7 @@ border-radius: 7px;
</widget> </widget>
</item> </item>
<item row="7" column="2"> <item row="7" column="2">
<widget class="QSpinBox" name="spinBox_7"/> <widget class="QSpinBox" name="sp_dct_bandwidth"/>
</item> </item>
<item row="4" column="0"> <item row="4" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
@ -977,13 +977,20 @@ border-radius: 7px;
</widget> </widget>
</item> </item>
<item row="5" column="2"> <item row="5" column="2">
<widget class="QSpinBox" name="spinBox_2"/> <widget class="QSpinBox" name="sp_v2">
<property name="minimum">
<number>500</number>
</property>
<property name="maximum">
<number>7000</number>
</property>
</widget>
</item> </item>
<item row="6" column="2"> <item row="6" column="2">
<widget class="QSpinBox" name="spinBox_3"/> <widget class="QSpinBox" name="sp_dct_center"/>
</item> </item>
<item row="8" column="2"> <item row="8" column="2">
<widget class="QSpinBox" name="spinBox_13"/> <widget class="QSpinBox" name="sp_f_rows"/>
</item> </item>
<item row="8" column="1"> <item row="8" column="1">
<widget class="QSlider" name="s_f_rows"> <widget class="QSlider" name="s_f_rows">
@ -1008,7 +1015,7 @@ border-radius: 7px;
<item row="5" column="1"> <item row="5" column="1">
<widget class="QSlider" name="s_v2"> <widget class="QSlider" name="s_v2">
<property name="minimum"> <property name="minimum">
<number>1</number> <number>500</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>7000</number> <number>7000</number>
@ -1045,7 +1052,7 @@ border-radius: 7px;
</widget> </widget>
</item> </item>
<item row="3" column="2"> <item row="3" column="2">
<widget class="QSpinBox" name="spinBox_12"/> <widget class="QSpinBox" name="sp_crop_center"/>
</item> </item>
<item row="9" column="1"> <item row="9" column="1">
<widget class="QSlider" name="s_beta"> <widget class="QSlider" name="s_beta">
@ -1071,7 +1078,7 @@ border-radius: 7px;
</widget> </widget>
</item> </item>
<item row="9" column="2"> <item row="9" column="2">
<widget class="QSpinBox" name="spinBox_14"/> <widget class="QSpinBox" name="sp_beta"/>
</item> </item>
</layout> </layout>
</item> </item>

View File

@ -403,8 +403,12 @@ class DeviceZero(Msg):
@dataclasses.dataclass @dataclasses.dataclass
class MidiMsg(Msg): class MidiMsg(Msg):
type: str type: str
channel: int channel: int = None
pitch: int pitch: int = None
control: int = None
value: int = None
velocity: int = None
note: int = None
@dataclasses.dataclass @dataclasses.dataclass

View File

@ -210,6 +210,11 @@ class RfMat:
def crop(self, t_start: int, t_end: int): def crop(self, t_start: int, t_end: int):
return self.copy(self.m[:, t_start:t_end]) return self.copy(self.m[:, t_start:t_end])
def crop_center(self, center: float, width: float):
mmin = max(0, int(center - width / 2))
mmax = min(self.duration, int(center + width / 2))
return self.crop(mmin, mmax)
def watermark(self, watermark=None): def watermark(self, watermark=None):
assert self.m.dtype == np.uint8 assert self.m.dtype == np.uint8
canvas = np.zeros(self.m.shape, dtype=np.uint8) canvas = np.zeros(self.m.shape, dtype=np.uint8)