diff --git a/main.py b/main.py
index 2b12076..abf606e 100644
--- a/main.py
+++ b/main.py
@@ -497,6 +497,62 @@ textedit_osc_out_log.setStyleSheet("""
}
""")
+# Container: container_midi_in_log
+container_midi_in_log = QWidget(None, Qt.WindowType.Window)
+container_midi_in_log.setWindowTitle("MIDI In — Incoming")
+container_midi_in_log.setStyleSheet("background-color: #1a1a1a;")
+container_midi_in_log.setFixedWidth(360)
+
+vstack_midi_in_log = QVBoxLayout(container_midi_in_log)
+vstack_midi_in_log.setContentsMargins(6, 6, 6, 6)
+vstack_midi_in_log.setSpacing(4)
+
+hstack_midi_in_log_header = QHBoxLayout()
+label_midi_in_log_title = QLabel("MIDI In — Incoming")
+label_midi_in_log_title.setStyleSheet("color: #ff8c00; font-weight: bold; font-size: 11px;")
+button_midi_in_clear = QPushButton("Clear")
+button_midi_in_clear.setFixedSize(45, 18)
+
+textedit_midi_in_log = QTextEdit()
+textedit_midi_in_log.setReadOnly(True)
+textedit_midi_in_log.setStyleSheet("""
+ QTextEdit {
+ background-color: #111111;
+ color: #ff8c00;
+ font-family: monospace;
+ font-size: 10px;
+ border: none;
+ }
+""")
+
+# Container: container_midi_out_log
+container_midi_out_log = QWidget(None, Qt.WindowType.Window)
+container_midi_out_log.setWindowTitle("MIDI Out — Outgoing")
+container_midi_out_log.setStyleSheet("background-color: #1a1a1a;")
+container_midi_out_log.setFixedWidth(360)
+
+vstack_midi_out_log = QVBoxLayout(container_midi_out_log)
+vstack_midi_out_log.setContentsMargins(6, 6, 6, 6)
+vstack_midi_out_log.setSpacing(4)
+
+hstack_midi_out_log_header = QHBoxLayout()
+label_midi_out_log_title = QLabel("MIDI Out — Outgoing")
+label_midi_out_log_title.setStyleSheet("color: #00bcd4; font-weight: bold; font-size: 11px;")
+button_midi_out_clear = QPushButton("Clear")
+button_midi_out_clear.setFixedSize(45, 18)
+
+textedit_midi_out_log = QTextEdit()
+textedit_midi_out_log.setReadOnly(True)
+textedit_midi_out_log.setStyleSheet("""
+ QTextEdit {
+ background-color: #111111;
+ color: #00bcd4;
+ font-family: monospace;
+ font-size: 10px;
+ border: none;
+ }
+""")
+
# MARK: PRESET BROWSER ROW MODULE
label_preset_browser_title = QLabel("Preset Browser")
hstack_preset_browser = QHBoxLayout()
@@ -986,7 +1042,25 @@ class MainWindow(QMainWindow):
button_osc_out_clear.clicked.connect(lambda: textedit_osc_out_log.clear())
container_osc_out_log.setVisible(False)
-
+
+ #MARK: MIDI IN LOG CONTAINER
+ hstack_midi_in_log_header.addWidget(label_midi_in_log_title)
+ hstack_midi_in_log_header.addStretch()
+ hstack_midi_in_log_header.addWidget(button_midi_in_clear)
+ vstack_midi_in_log.addLayout(hstack_midi_in_log_header)
+ vstack_midi_in_log.addWidget(textedit_midi_in_log)
+ button_midi_in_clear.clicked.connect(lambda: textedit_midi_in_log.clear())
+ container_midi_in_log.setVisible(False)
+
+ #MARK: MIDI OUT LOG CONTAINER
+ hstack_midi_out_log_header.addWidget(label_midi_out_log_title)
+ hstack_midi_out_log_header.addStretch()
+ hstack_midi_out_log_header.addWidget(button_midi_out_clear)
+ vstack_midi_out_log.addLayout(hstack_midi_out_log_header)
+ vstack_midi_out_log.addWidget(textedit_midi_out_log)
+ button_midi_out_clear.clicked.connect(lambda: textedit_midi_out_log.clear())
+ container_midi_out_log.setVisible(False)
+
main_layout.addWidget(make_separator())
# MARK: TRANSPORT ROW CONTROLS
@@ -1185,7 +1259,9 @@ class MainWindow(QMainWindow):
check_midi_port_availability()
midi_receiver.midi_in_signal.connect(lambda msg: make_activity_blink(activity_indicator_midi_in))
- midi_receiver.midi_out_signal.connect(lambda: make_activity_blink(activity_indicator_midi_out))
+ midi_receiver.midi_in_signal.connect(log_midi_incoming)
+ midi_receiver.midi_out_signal.connect(lambda _: make_activity_blink(activity_indicator_midi_out))
+ midi_receiver.midi_out_signal.connect(log_midi_outgoing)
midi_receiver.transport_out_signal.connect(lambda: make_activity_blink(activity_indicator_transport_out))
midi_receiver.hw_cc_signal.connect(midi_message_route_hw_to_output_cc)
@@ -1357,34 +1433,46 @@ def set_port_transport_output(port_name):
check_midi_port_availability()
save_io_config()
+def _append_log(textedit, line):
+ textedit.append(line)
+ if textedit.document().blockCount() > 200:
+ cursor = textedit.textCursor()
+ cursor.movePosition(cursor.MoveOperation.Start)
+ cursor.select(cursor.SelectionType.LineUnderCursor)
+ cursor.removeSelectedText()
+ cursor.deleteChar()
+
+def _midi_msg_str(msg):
+ if len(msg) >= 3:
+ status = msg[0] & 0xF0
+ ch = (msg[0] & 0x0F) + 1
+ if status == 0xB0:
+ return f"CC{msg[1]} CH{ch} [{msg[2]}]"
+ return " ".join(f"{b:02X}" for b in msg)
+
+def log_midi_incoming(msg):
+ from datetime import datetime
+ ts = datetime.now().strftime("%H:%M:%S")
+ line = f"{ts} {_midi_msg_str(msg)}"
+ _append_log(textedit_midi_in_log, line)
+
+def log_midi_outgoing(msg):
+ from datetime import datetime
+ ts = datetime.now().strftime("%H:%M:%S")
+ line = f"{ts} {_midi_msg_str(msg)}"
+ _append_log(textedit_midi_out_log, line)
+
def log_osc_incoming(addr, val):
from datetime import datetime
ts = datetime.now().strftime("%H:%M:%S")
line = f"{ts} {addr} {val}"
- textedit_osc_in_log.append(line)
- doc = textedit_osc_in_log.document()
- if doc.blockCount() > 200:
- cursor = textedit_osc_in_log.textCursor()
- cursor.movePosition(cursor.MoveOperation.Start)
- cursor.select(cursor.SelectionType.LineUnderCursor)
- cursor.removeSelectedText()
- cursor.deleteChar()
+ _append_log(textedit_osc_in_log, line)
def log_osc_outgoing(addr, val):
-
from datetime import datetime
-
ts = datetime.now().strftime("%H:%M:%S")
-
line = f"{ts} {addr} {val}"
- textedit_osc_out_log.append(line)
- doc = textedit_osc_out_log.document()
- if doc.blockCount() > 200:
- cursor = textedit_osc_out_log.textCursor()
- cursor.movePosition(cursor.MoveOperation.Start)
- cursor.select(cursor.SelectionType.LineUnderCursor)
- cursor.removeSelectedText()
- cursor.deleteChar()
+ _append_log(textedit_osc_out_log, line)
def update_osc_server_ip(ip):
set_osc_target(ip=ip.strip() or "127.0.0.1")
@@ -2191,18 +2279,29 @@ def midi_message_route_hw_to_output_cc(fader_uid, value):
def log_ui_show_hide(checked):
container_osc_in_log.setVisible(checked)
container_osc_out_log.setVisible(checked)
+ container_midi_in_log.setVisible(checked)
+ container_midi_out_log.setVisible(checked)
log_ui_window_calc()
def log_ui_window_calc():
geo = app_window.frameGeometry()
h = geo.height()
- container_osc_in_log.setFixedHeight(h)
+ half = h // 2 - 2
+ # Left column: OSC In (top) + OSC Out (bottom)
+ container_osc_in_log.setFixedWidth(360)
+ container_osc_in_log.setFixedHeight(half)
container_osc_in_log.move(geo.right() + 4, geo.top())
container_osc_out_log.setFixedWidth(360)
- container_osc_out_log.setFixedHeight(h // 2)
- container_osc_out_log.move(geo.right() + 4, geo.top() + h // 2 + 4)
- container_osc_in_log.setFixedHeight(h // 2 - 4)
+ container_osc_out_log.setFixedHeight(half)
+ container_osc_out_log.move(geo.right() + 4, geo.top() + half + 4)
+ # Right column: MIDI In (top) + MIDI Out (bottom)
+ container_midi_in_log.setFixedWidth(360)
+ container_midi_in_log.setFixedHeight(half)
+ container_midi_in_log.move(geo.right() + 368, geo.top())
+ container_midi_out_log.setFixedWidth(360)
+ container_midi_out_log.setFixedHeight(half)
+ container_midi_out_log.move(geo.right() + 368, geo.top() + half + 4)
def start_osc_server(port=9000):
global _osc_server, _osc_thread
diff --git a/midi_receiver.py b/midi_receiver.py
index eff0c72..71e41ad 100644
--- a/midi_receiver.py
+++ b/midi_receiver.py
@@ -2,7 +2,7 @@ from PyQt6.QtCore import QObject, pyqtSignal
class MidiReceiver(QObject):
midi_in_signal = pyqtSignal(list)
- midi_out_signal = pyqtSignal()
+ midi_out_signal = pyqtSignal(list)
transport_out_signal = pyqtSignal()
hw_cc_signal = pyqtSignal(str, int)
diff --git a/midi_sender.py b/midi_sender.py
index 8dfd305..1feb6b4 100644
--- a/midi_sender.py
+++ b/midi_sender.py
@@ -14,12 +14,14 @@ print("Available MIDI ports:", available_ports)
def send_cc(cc_number, value, channel=None):
ch = (channel - 1) if channel is not None else MIDI_CH
+ msg = [0xB0 | ch, cc_number, value]
if midi_out.is_port_open():
- midi_out.send_message([0xB0 | ch, cc_number, value])
- midi_receiver.midi_out_signal.emit()
+ midi_out.send_message(msg)
+ midi_receiver.midi_out_signal.emit(msg)
def send_transport_cc(cc_number, value):
+ msg = [0xB0 | TRANSPORT_MIDI_CH, cc_number, value]
if transport_midi_out.is_port_open():
- transport_midi_out.send_message([0xB0 | TRANSPORT_MIDI_CH, cc_number, value])
- midi_receiver.transport_out_signal.emit()
\ No newline at end of file
+ transport_midi_out.send_message(msg)
+ midi_receiver.midi_out_signal.emit(msg)
\ No newline at end of file
diff --git a/presets/presets.json b/presets/presets.json
index 938f502..956ba9d 100644
--- a/presets/presets.json
+++ b/presets/presets.json
@@ -483,8 +483,8 @@
],
"transport_show_hide": false,
"io_config": {
- "midi_out": "Not Connected",
- "midi_in": "VMPK Output",
+ "midi_out": "IAC Driver Bus 1",
+ "midi_in": "Not Connected",
"transport_out": "Not Connected",
"osc_ip": "127.0.0.1",
"osc_listen_port": 9000,