jdxi_editor.midi.file.controller ================================ .. py:module:: jdxi_editor.midi.file.controller .. autoapi-nested-parse:: MIDI File Controller Module Manages MIDI file operations including saving, loading, and pattern conversion. Handles tempo management, bar detection, and note-to-button mapping. Classes ------- .. autoapisummary:: jdxi_editor.midi.file.controller.MidiFileControllerConfig jdxi_editor.midi.file.controller.MidiFileLoadResult jdxi_editor.midi.file.controller.MidiFileController Module Contents --------------- .. py:class:: MidiFileControllerConfig(ticks_per_beat: int = 480, beats_per_measure: int = 4, default_bpm: int = 120, default_velocity: int = 100) Configuration for MIDI file controller. .. py:attribute:: ticks_per_beat :value: 480 .. py:attribute:: beats_per_measure :value: 4 .. py:attribute:: default_bpm :value: 120 .. py:attribute:: default_velocity :value: 100 .. py:property:: ticks_per_measure :type: int Calculate ticks per bar. .. py:class:: MidiFileLoadResult(success: bool, num_bars: int = 0, notes_loaded: int = 0, tempo_bpm: Optional[int] = None, error_message: Optional[str] = None) Result of loading a MIDI file. .. py:attribute:: success .. py:attribute:: num_bars :value: 0 .. py:attribute:: notes_loaded :value: 0 .. py:attribute:: tempo_bpm :value: None .. py:attribute:: error_message :value: None .. py:class:: MidiFileController(config: Optional[MidiFileControllerConfig] = None, midi_converter: Optional[jdxi_editor.midi.conversion.note.MidiNoteConverter] = None, scope: str = 'MidiFileController') Manages MIDI file I/O operations for pattern sequencer. Handles: - Loading MIDI files and converting to patterns - Saving patterns to MIDI files - Tempo management - Bar/measure detection - MIDI note-to-button mapping .. py:attribute:: CHANNEL_TO_ROW .. py:attribute:: ROW_TO_CHANNEL .. py:attribute:: config .. py:attribute:: midi_converter :value: None .. py:attribute:: scope :value: 'MidiFileController' .. py:attribute:: midi_file :type: Optional[mido.MidiFile] :value: None .. py:attribute:: current_bpm :value: 120 .. py:attribute:: on_file_loaded :type: Optional[Callable[[MidiFileLoadResult], None]] :value: None .. py:attribute:: on_file_saved :type: Optional[Callable[[str], None]] :value: None .. py:attribute:: on_tempo_changed :type: Optional[Callable[[int], None]] :value: None .. py:method:: create_new_file() -> mido.MidiFile Create a new MIDI file with default settings. :return: New MidiFile object .. py:method:: set_tempo(bpm: int) -> None Set the MIDI file tempo. Updates the current file's tempo and triggers callback. :param bpm: Tempo in beats per minute (20-300) .. py:method:: get_tempo() -> int Get the current MIDI file tempo. :return: Tempo in BPM .. py:method:: save_pattern(filename: str, measures: List, pattern_name: Optional[str] = None) -> bool Save pattern to a MIDI file. Creates a MIDI file from the current pattern with proper formatting. :param filename: Path to save file :param measures: List of PatternMeasure objects to save :param pattern_name: Optional name for the pattern (used as metadata) :return: True if successful, False otherwise .. py:method:: load_pattern(filename: str, measures_container: Optional[List] = None) -> MidiFileLoadResult Load pattern from a MIDI file. Parses MIDI file and returns data for populating measures/buttons. :param filename: Path to MIDI file :param measures_container: Optional list to populate with loaded measures :return: MidiFileLoadResult with load status and metadata .. py:method:: load_from_midi_file_editor(midi_file_editor) -> MidiFileLoadResult Load pattern from a MidiFileEditor instance. Useful for sharing MIDI files between editor windows. :param midi_file_editor: MidiFileEditor instance with loaded file :return: MidiFileLoadResult with load status .. py:method:: _load_from_midi_file_object(midi_file: mido.MidiFile) -> MidiFileLoadResult Load pattern from a MidiFile object (internal method). :param midi_file: MidiFile instance :return: MidiFileLoadResult with load status .. py:method:: _detect_bars_from_midi(midi_file: mido.MidiFile) -> int Detect the number of bars in a MIDI file. Counts the maximum absolute time and divides by ticks per bar. :param midi_file: MidiFile to analyze :return: Number of bars detected (minimum 1) .. py:method:: _parse_midi_file(midi_file: mido.MidiFile, ppq: int, ticks_per_measure: int) -> List[Dict] Parse MIDI file and extract note events. Returns list of note dictionaries with timing and metadata. :param midi_file: MidiFile to parse :param ppq: Ticks per beat from the file :param ticks_per_measure: Ticks per bar calculation :return: List of parsed note events .. py:method:: _extract_tempo_from_midi(midi_file: mido.MidiFile) -> Optional[int] Extract tempo from MIDI file. Searches all tracks for the first SET_TEMPO message. :param midi_file: MidiFile to search :return: Tempo in BPM, or None if not found .. py:method:: _add_tempo_to_track(track: mido.MidiTrack, bpm: int) -> None Add tempo message to a track. :param track: MidiTrack to add tempo to :param bpm: Tempo in BPM .. py:method:: _get_button_note_spec(button) Get note specification from a button. :param button: SequencerButton instance :return: NoteButtonSpec with note, duration_ms, velocity, is_active .. py:method:: set_config(config: MidiFileControllerConfig) -> None Update controller configuration. :param config: New configuration .. py:method:: set_midi_converter(converter: jdxi_editor.midi.conversion.note.MidiNoteConverter) -> None Set or update the MIDI converter. :param converter: MidiNoteConverter instance .. py:method:: get_current_file() -> Optional[mido.MidiFile] Get the currently loaded MIDI file. :return: MidiFile instance or None