jdxi_editor.midi.playback.controller

Pattern Playback Controller Module

Manages MIDI pattern playback using the PlaybackEngine. Handles: - Starting/stopping/pausing playback - Building MIDI files from patterns - UI synchronization during playback - Muting/unmuting channels - Shuffle play functionality

Classes

PlaybackConfig

Configuration for playback controller.

PlaybackPosition

Represents the current playback position.

PatternPlaybackController

Controls pattern playback and synchronization.

Module Contents

class jdxi_editor.midi.playback.controller.PlaybackConfig(ticks_per_beat: int = 480, beats_per_measure: int = 4, measure_beats: int = 16, default_bpm: int = 120, playback_interval_ms: int = 20)[source]

Configuration for playback controller.

ticks_per_beat = 480[source]
beats_per_measure = 4[source]
measure_beats = 16[source]
default_bpm = 120[source]
playback_interval_ms = 20[source]
property ticks_per_measure: int[source]

Calculate ticks per bar.

class jdxi_editor.midi.playback.controller.PlaybackPosition(global_step: int = 0, bar_index: int = 0, step_in_bar: int = 0)[source]

Represents the current playback position.

global_step = 0[source]
bar_index = 0[source]
step_in_bar = 0[source]
class jdxi_editor.midi.playback.controller.PatternPlaybackController(config: PlaybackConfig | None = None, playback_engine: picomidi.playback.engine.PlaybackEngine | None = None, scope: str = 'PatternPlaybackController')[source]

Bases: PySide6.QtCore.QObject

Controls pattern playback and synchronization.

Manages: - PlaybackEngine operation - UI updates during playback (bar/step highlighting) - Muting/unmuting channels - Pause/resume functionality - Shuffle play

config[source]
playback_engine[source]
scope = 'PatternPlaybackController'[source]
is_playing = False[source]
is_paused = False[source]
current_bpm = 120[source]
current_position[source]
last_bar_index = -1[source]
last_step_in_bar = -1[source]
muted_channels: List[int] = [][source]
timer: PySide6.QtCore.QTimer | None = None[source]
worker[source]
on_playback_started: Callable[[], None] | None = None[source]
on_playback_stopped: Callable[[], None] | None = None[source]
on_playback_paused: Callable[[], None] | None = None[source]
on_playback_resumed: Callable[[], None] | None = None[source]
on_position_changed: Callable[[PlaybackPosition], None] | None = None[source]
on_bar_changed: Callable[[int], None] | None = None[source]
on_step_changed: Callable[[int], None] | None = None[source]
on_midi_event: Callable[[mido.Message], Any] | None = None[source]
start_playback(measures: List, bpm: int | None = None) bool[source]

Start pattern playback.

Parameters:
  • measures – List of PatternMeasure objects to play

  • bpm – Optional tempo override

Returns:

True if playback started successfully

_on_worker_finished() None[source]

Handle worker.finished when engine stops playing.

stop_playback() None[source]

Stop pattern playback.

pause_playback() None[source]

Pause pattern playback (can be resumed).

resume_playback() None[source]

Resume paused playback.

toggle_pause() None[source]

Toggle pause/resume state.

shuffle_play(measures: List, bpm: int | None = None) bool[source]

Select a random bar and start playback.

Parameters:
  • measures – List of PatternMeasure objects

  • bpm – Optional tempo override

Returns:

True if playback started

mute_channel(channel: int, mute: bool = True) None[source]

Mute or unmute a specific MIDI channel.

Parameters:
  • channel – MIDI channel (0-15)

  • mute – True to mute, False to unmute

mute_row(row: int, mute: bool = True) None[source]

Mute or unmute a sequencer row.

Row to channel mapping: - Row 0: Channel 0 (Digital Synth 1) - Row 1: Channel 1 (Digital Synth 2) - Row 2: Channel 2 (Analog Synth) - Row 3: Channel 9 (Drums)

Parameters:
  • row – Row index (0-3)

  • mute – True to mute, False to unmute

toggle_mute_row(row: int) bool[source]

Toggle mute for a row.

Parameters:

row – Row index (0-3)

Returns:

New mute state (True if now muted)

is_row_muted(row: int) bool[source]

Check if a row is muted.

Parameters:

row – Row index (0-3)

Returns:

True if row is muted

process_playback_tick(total_steps: int) PlaybackPosition | None[source]

Process a playback timer tick.

Called by timer. Updates engine and returns current position.

Parameters:

total_steps – Total steps in pattern (bars * steps_per_bar)

Returns:

Updated PlaybackPosition or None if playback has stopped

get_current_position() PlaybackPosition[source]

Get the current playback position.

Returns:

PlaybackPosition object

get_playback_state() Dict[str, bool][source]

Get the current playback state.

Returns:

Dictionary with is_playing, is_paused, muted_channels

set_tempo(bpm: int) None[source]

Set playback tempo.

Parameters:

bpm – Tempo in BPM

reload_playback_with_tempo(measures: List, bpm: int) bool[source]

Rebuild MIDI with new tempo and resume from current position. Call when tempo changes during playback.

Parameters:
  • measures – Current pattern measures

  • bpm – New tempo in BPM

Returns:

True if reload succeeded

get_tempo() int[source]

Get current playback tempo.

_build_midi_file_for_playback(measures: List) mido.MidiFile[source]

Build a MIDI file from the pattern for playback.

Parameters:

measures – List of PatternMeasure objects

Returns:

MidiFile ready for playback

_collect_sequencer_events(measures: List) List[picomidi.sequencer.event.SequencerEvent][source]

Collect all note events from measures.

Parameters:

measures – List of PatternMeasure objects

Returns:

List of SequencerEvent objects

_sequencer_event(channel: int, duration_ticks: int, spec: picomidi.ui.widget.button.note.NoteButtonEvent, tick: int, velocity: int) picomidi.sequencer.event.SequencerEvent[source]

add sequencer event

_ms_to_ticks(duration_ms: float) int[source]

Convert milliseconds to MIDI ticks.

Formula: ticks = (duration_ms / 1000) * (bpm / 60) * ticks_per_beat

Parameters:

duration_ms – Duration in milliseconds

Returns:

Duration in MIDI ticks

_get_button_note_spec(button)[source]

Get note specification from a button.

Parameters:

button – SequencerButton instance

Returns:

NoteButtonSpec with note, duration_ms, velocity, is_active

_get_engine_tick() int[source]

Get the current tick position from the playback engine.

Returns:

Absolute tick position

_start_timer() None[source]

Start the playback timer.

_stop_timer() None[source]

Stop the playback timer.

_update_timer_interval() None[source]

Update timer interval based on current tempo.

set_config(config: PlaybackConfig) None[source]

Update controller configuration.

Parameters:

config – New PlaybackConfig

get_engine() picomidi.playback.engine.PlaybackEngine[source]

Get the underlying PlaybackEngine.

Useful for advanced control if needed.

Returns:

PlaybackEngine instance