jdxi_editor.ui.editors.midi_player.editor

MIDI Player for JDXI Editor

Attributes

QApplication

QObject

Signal

Slot

Classes

MidiFileAttrs

Midi File Attributes

MidiFilePlayer

Midi File Editor

Module Contents

jdxi_editor.ui.editors.midi_player.editor.QApplication = None[source]
jdxi_editor.ui.editors.midi_player.editor.QObject = None[source]
jdxi_editor.ui.editors.midi_player.editor.Signal = None[source]
jdxi_editor.ui.editors.midi_player.editor.Slot = None[source]
class jdxi_editor.ui.editors.midi_player.editor.MidiFileAttrs[source]

Midi File Attributes

TICKS_PER_BEAT = 'ticks_per_beat'[source]
class jdxi_editor.ui.editors.midi_player.editor.MidiFilePlayer(midi_helper: jdxi_editor.midi.io.helper.MidiIOHelper | None = None, parent: jdxi_editor.ui.common.QWidget = None, preset_helper: jdxi_editor.ui.preset.helper.JDXiPresetHelper = None)[source]

Bases: jdxi_editor.ui.editors.synth.editor.SynthEditor

Midi File Editor

BUFFER_WINDOW_SECONDS = 30.0[source]
midi_helper = None[source]
tick_duration = None[source]
specs: dict | None = None[source]
_last_position_label: PySide6.QtWidgets.QLabel | None = None[source]
parent: jdxi_editor.ui.common.QWidget = None[source]
preset_helper: jdxi_editor.ui.preset.helper.JDXiPresetHelper = None[source]
midi_state: jdxi_editor.midi.playback.state.MidiPlaybackState[source]
playback_engine: picomidi.playback.engine.PlaybackEngine[source]
midi_analyzer: jdxi_editor.ui.editors.midi_player.midi_analyzer.MidiAnalyzer[source]
midi_playback_worker: picomidi.playback.worker.MidiPlaybackWorker[source]
midi_total_ticks: int | None = None[source]
midi_port[source]
current_tempo_bpm = None[source]
midi_preferred_channels[source]
usb_recorder[source]
automation[source]
ui[source]
transport: jdxi_editor.ui.widgets.transport.transport.TransportWidget[source]
midi_file_group: jdxi_editor.ui.widgets.midi_file_group.MidiFileGroup[source]
event_suppression_group: jdxi_editor.ui.widgets.event_suppression_group.EventSuppressionGroup[source]
classification_group: jdxi_editor.ui.widgets.classification_group.ClassificationGroup[source]
mute_channels_group: jdxi_editor.ui.widgets.mute_channels_group.MuteChannelsGroup[source]
midi_file[source]
midi_timer_init()[source]

Initialize or reinitialize the MIDI playback timer. Ensures previous connections are safely removed.

ui_ensure_timer_connected()[source]

ui_ensure_timer_connected

Returns:

Ensure the midi_play_next_event is connected to midi.timer.timeout

ui_init()[source]

Initialize the UI for the MidiPlayer.

_add_base_widget_to_editor()[source]

Add base widget to editor’s layout

_build_left_panel() jdxi_editor.ui.common.QVBoxLayout[source]

Build left panel

_build_right_panel()[source]

Build right panel

detect_and_assign_drum_tracks() None[source]

Detect drum tracks in the loaded MIDI file and assign Channel 10 (1-based) to them.

This analyzes all tracks using heuristics and automatically sets the channel spinboxes for tracks identified as drum tracks.

classify_and_assign_tracks() None[source]

classify and assign tracks

_validate_midi_loaded() bool[source]

validate that a MIDI file is loaded

_detect_drum_tracks() list[int][source]

Return track indices identified as drum tracks.

_classify_tracks(drum_indices: list[int])[source]

Classify non-drum tracks into Bass, Keys/Guitars, Strings.

_apply_channel_assignments(classifications) dict[str, list[str]][source]

apply channel assignments

_handle_unclassified_tracks(classifications, updated: dict[str, list[str]])[source]

Handle unclassified

_has_classified_tracks(updated_tracks: dict) bool[source]

Return True if at least one track was classified (excluding unclassified).

_show_no_tracks_message() None[source]

Show the no-tracks-classified message box.

_build_classification_message(updated: dict[str, list[str]]) str[source]

build classification message

_show_info_message(title: str, message: str) None[source]

show info dialog

_show_warning(title: str, message: str) None[source]

show warning

_handle_classification_error(ex: Exception) None[source]

handle classification error

init_automation_usb_grid() PySide6.QtWidgets.QGridLayout[source]

Create a grid layout containing Automation, USB Port, and USB File controls.

insert_program_change_current_position() None[source]

Insert Bank Select (CC#0, CC#32) and Program Change at the current slider time.

_build_message(message_type: str, channel: int, value: int = None, program: int = None) mido.Message[source]

Build message

_find_track_for_channel(channel: int) int[source]
_insert_messages_at_abs_tick(track: mido.MidiTrack, abs_tick_target: int, new_msgs: list[mido.Message]) None[source]

Insert a list of messages at a given absolute tick, preserving delta times.

_toggle_channel_mute(channel: int, is_muted: bool, btn) None[source]

Toggle mute state for a specific MIDI channel. Updates both the track viewer and the player’s muted channels state.

Parameters:
  • channel – int MIDI channel (1-16)

  • is_muted – bool is the channel muted?

_sync_mute_buttons_from_track_viewer() None[source]

Sync mute channel buttons in the USB file controls with the track viewer’s state. Called when MIDI file is loaded or track viewer’s mute state changes.

apply_all_track_changes() None[source]

Apply all Track Name and MIDI Channel changes. Calls the track viewer’s apply_all_track_changes method.

apply_channel_presets() None[source]

Send live MIDI Program Changes to the synth and insert presets into the MIDI file. Ch 1→Picked Bass, Ch 2→Piano, Ch 3→JP8 Strings.

_create_transport_control(spec: picomidi.ui.widget.transport.spec.TransportSpec, layout: PySide6.QtWidgets.QHBoxLayout, button_group: PySide6.QtWidgets.QButtonGroup | None) None[source]

Create a transport button + label row

update_tempo_us_from_worker(tempo_us: int) None[source]

update_tempo_us_from_worker

Parameters:

tempo_us – int tempo in microseconds e.g 500_000

Returns:

None

update_playback_worker_tempo_us(tempo_us: int) None[source]

update_playback_worker_tempo_us

Parameters:

tempo_us – tempo in microseconds e.g 500_000

Returns:

None

setup_worker()[source]

setup_worker

Returns:

None

Setup the worker and thread for threaded playback using QTimer

midi_playback_worker_stop()[source]

midi_playback_worker_stop

Returns:

None

on_suppress_program_changes_toggled(state: PySide6.QtCore.Qt.CheckState) None[source]

on_suppress_program_changes_toggled

Parameters:

state – Qt.CheckState

Returns:

None

on_suppress_control_changes_toggled(state: PySide6.QtCore.Qt.CheckState)[source]

on_suppress_control_changes_toggled

Parameters:

state – Qt.CheckState

Returns:

midi_save_file() None[source]

midi_save_file

Returns:

None

Save the current MIDI file to disk.

midi_load_file() None[source]

Load a MIDI file and initialize parameters

midi_load_file_from_path(file_path: str) None[source]

Load a MIDI file from a given path and initialize parameters.

Parameters:

file_path – Path to the MIDI file

calculate_tick_duration()[source]

calculate_tick_duration

Returns:

Calculate the duration of a single MIDI tick in seconds.

calculate_duration() None[source]

calculate_duration

Returns:

None

Accurate Total Duration Calculation

midi_channel_select() None[source]

Select a suitable MIDI channel for playback from the file.

midi_extract_events() None[source]

midi_extract_events

Returns:

None

Extract events from the MIDI file and store them in the midi_state.

detect_initial_tempo() dict[int, int][source]

Detect initial tempo from the file and set midi_state.tempo_initial.

Returns:

dict[track_number, tempo_usec] for tracks that have set_tempo

ui_display_set_tempo_usecs(tempo_usecs: int) None[source]

ui_display_set_tempo_usecs

Parameters:

tempo_usecs – int tempo in microseconds

Returns:

None

Set the tempo in the UI and log it.

set_display_tempo_bpm(tempo_bpm: float) None[source]

set_display_tempo_bpm

Parameters:

tempo_bpm – float tempo in BPM

Returns:

None

Set the tempo in the UI and log it. Also pushes tempo to Pattern Sequencer so both editors stay in sync when using the same playback pipeline.

update_upper_display_with_tempo_and_bar(elapsed_time: float | None = None) None[source]

Update the upper digital with tempo and optionally bar number.

Parameters:

elapsed_time – Optional elapsed time for bar calculation. If None, uses current playback state.

turn_off_effects() None[source]

Turn off all effects (Effect 1, Effect 2, Delay, Reverb) when starting playback. This prevents distortion and other effects from being accidentally enabled.

midi_playback_start()[source]

Start playback of the MIDI file from the beginning (or resume if paused).

_test_paused_state_then_rewind()[source]
_connect_ui()[source]

Connect UI update

_disconnect_ui()[source]

Disconnect all existing connections first

_connect_worker()[source]
setup_playback_worker()[source]

setup_playback_worker

Returns:

None

Setup the MIDI playback worker (prepare buffered messages, etc.) Loads engine and wires on_event; worker uses engine.process_until_now().

start_playback_worker()[source]

start_playback_worker

Returns:

None

Start the timer for actual playback.

setup_and_start_playback_worker()[source]

setup_and_start_playback_worker

Returns:

None

Setup the MIDI playback worker and start the timer.

initialize_midi_state() None[source]

Initialize muted tracks, muted channels, and buffered messages.

calculate_start_tick() int | None[source]

Calculate the start tick based on elapsed playback time. :return: Start tick or None if an error occurs.

is_track_muted(track_index: int) bool[source]

is_track_muted

Parameters:

track_index – Index of the track to check.

Returns:

True if the track is muted, otherwise False.

Check if the track is muted.

is_channel_muted(channel_index: int) bool[source]

is_channel_muted

Parameters:

channel_index – Index of the track to check.

Returns:

True if the channel is muted, otherwise False.

Check if the channel is muted.

get_muted_tracks()[source]

get_muted_tracks

Returns:

None

Get the muted tracks from the MIDI track viewer.

get_muted_channels()[source]

get_muted_channels

Returns:

None

Get the muted channels from the MIDI track viewer.

on_tempo_usecs_changed(tempo: int)[source]

on_tempo_usecs_changed

Parameters:

tempo – int

Returns:

None

on_tempo_bpm_changed(bpm: float)[source]

on_tempo_bpm_changed

Parameters:

bpm – float

Returns:

None

midi_play_next_event()[source]

UI update: Update slider and label to reflect playback progress.

ui_midi_file_position_slider_set_position(elapsed_time: float) None[source]

ui_midi_file.position_slider_set_position

Parameters:

elapsed_time – float

Returns:

None

Update the slider position and label based on elapsed time.

midi_scrub_position()[source]

Updates the MIDI playback state based on the scrub position.

is_midi_ready() bool[source]

Checks if the MIDI file and events are available.

stop_playback() None[source]

Stops playback and resets the paused state.

get_target_time() float[source]

Retrieves the target time from the slider and logs it.

update_event_index(target_time: float) None[source]

Finds and updates the event index based on the target time.

update_playback_start_time(target_time: float) None[source]

Adjusts the playback start time based on the scrub position. Uses target_time (slider value in seconds) directly so elapsed_time matches the scrubbed position and advances correctly during playback.

stop_all_notes() None[source]

Sends Control Change 123 and note_off messages to silence all notes.

prepare_for_playback() None[source]

Clears the event buffer and starts the playback worker.

_connect_to_ui()[source]

Connect UI update

_connect_to_worker()[source]

Connect worker if available

midi_playback_stop()[source]

Stops playback and resets everything to the beginning.

stop_playback_worker()[source]

Stops and disconnects the playback worker.

reset_midi_state()[source]

Resets MIDI state variables.

reset_tempo()[source]

Resets the tempo to the initial value.

clear_active_notes() None[source]

Clears the active notes.

log_event_buffer() None[source]

log_event_buffer

Returns:

None

Logs the event buffer for debugging.

perform_profiling() None[source]

No-op: per-playback profiling was removed to avoid skewing app-wide profiles.

midi_play_next_event_disconnect() None[source]

midi_play_next_event_disconnect

Returns:

None

Disconnect the midi_play_next_event from the timer’s timeout signal.

midi_playback_worker_disconnect() None[source]

midi_playback_worker_disconnect

Returns:

None

Disconnect the midi_playback_worker.do_work from the timer’s timeout signal.

ui_position_slider_reset() None[source]

position_slider_reset

Returns:

None

Reset the position slider and label to initial state.

midi_file_position_label_update_time(time_seconds: float | None = None) None[source]

midi_file_position_label_update_time

Parameters:

time_seconds – float, optional

Returns:

None

calculate_current_bar(elapsed_time: float | None = None) float | None[source]

Calculate the current bar number based on elapsed playback time.

Parameters:

elapsed_time – Optional elapsed time in seconds. If None, calculates from current playback state.

Returns:

Current bar number (1-based) or None if calculation fails.

ui_position_label_set_time(elapsed_time: float | None = None) None[source]

Update the position label with formatted elapsed time and total duration. Caps elapsed_time to total duration to prevent overflow digital. Also updates the bar number in the DigitalTitle upper digital.

update_position_text(elapsed_str: str, total_str: str)[source]

update position text

midi_playback_pause_toggle()[source]

midi_playback_pause_toggle

Returns:

None

Toggle pause and resume playback.

_resume_playback()[source]

Resuming playback

_pause_playback()[source]

Pausing playback

midi_playback_worker_handle_result(result=None)[source]

Handle the result from the worker. This can be used to update the UI or perform further actions. :param result: The result from the worker

_get_pattern_sequencer_editor()[source]

Return the Pattern Sequencer editor instance if available, else None.

_push_tempo_to_pattern_sequencer(tempo_bpm: float) None[source]

Push current display tempo to Pattern Sequencer so it stays in sync with playback pipeline.

_notify_pattern_sequencer_file_loaded() None[source]

Notify Pattern Sequencer that a MIDI file has been loaded.

_build_specs() dict[str, Any][source]

build specs for the Midi file player

_build_button_specs() dict[str, picoui.specs.widgets.ButtonSpec][source]
_build_message_box_specs() dict[str, picoui.specs.widgets.MessageBoxSpec][source]

Assemble all static message box specs for the MIDI player.

_build_checkbox_specs() dict[str, picoui.specs.widgets.CheckBoxSpec][source]

build check box specs (event suppression moved to EventSuppressionGroup)