Source code for jdxi_editor.midi.data.arpeggio.arpeggio

"""
Arpeggiator Configuration Module

This module defines the settings, data structures, and parameter ranges for an arpeggiator.
It provides enumerations, default values, and data classes to represent various arpeggiator
configurations, including grid timing, note duration, motif patterns, and styles.

### Contents:
- **Arpeggio Settings**:
  - `arp_grid`: Available grid timing values (e.g., 1/4, 1/8, etc.).
  - `arp_duration`: Note duration options as percentages.
  - `arp_motif`: Various motif patterns for the arpeggiator.
  - `arp_style`: A collection of predefined arpeggiator styles.

- **Arpeggio Parameter Ranges and Defaults**:
  - `Arpeggio`: Defines valid parameter ranges and default values.
  - `ArpeggioPatch`: A dataclass representing a complete arpeggio configuration.

- **Enumerations**:
  - `ArpeggioGrid`: Grid timing options.
  - `ArpeggioDuration`: Possible note durations.
  - `ArpeggioMotif`: Arpeggiator motif patterns.
  - `ArpeggioParameters`: Parameter identifiers used in arpeggiator control.

### Usage:
This module can be used to configure an arpeggiator in a MIDI editor or synthesizer
application. The `ArpeggioPatch` class allows structured representation and validation of
arpeggiator settings, ensuring proper configuration.

"""

from dataclasses import dataclass
from enum import Enum
from typing import List, Optional


@dataclass
[docs] class ArpeggioPatch: """Complete arpeggiator patch data""" # Common parameters
[docs] switch: bool = False
[docs] style: int = 0
[docs] octave: int = 0
[docs] grid: int = 1
[docs] duration: int = 50
[docs] motif: int = 0
[docs] key: int = 0
# Pattern data
[docs] patterns: Optional[List[int]] = None
[docs] rhythms: Optional[List[int]] = None
[docs] notes: Optional[List[int]] = None
[docs] def __post_init__(self) -> None: """Initialize pattern data""" if self.patterns is None: self.patterns = [0] * 4 if self.rhythms is None: self.rhythms = [64] * 4 if self.notes is None: self.notes = [60] * 4 # Middle C
[docs] class ArpeggioMotif(Enum): """Arpeggio motif values"""
[docs] UP_L = 0 # UP/L
[docs] UP_H = 1 # UP/H
[docs] UP_NORM = 2 # UP/_
[docs] DOWN_L = 3 # dn/L
[docs] DOWN_H = 4 # dn/H
[docs] DOWN_NORM = 5 # dn/_
[docs] UP_DOWN_L = 6 # Ud/L
[docs] UP_DOWN_H = 7 # Ud/H
[docs] UP_DOWN_NORM = 8 # Ud/_
[docs] RANDOM_L = 9 # rn/L
[docs] RANDOM_NORM = 10 # rn/_
[docs] PHRASE = 11 # PHRASE
@property
[docs] def display_name(self) -> str: """Get display name for grid value""" names = { 0: "Up (L)", 1: "Up (L&H)", 2: "Up (_)", 3: "Down (L)", 4: "Down (L&H)", 5: "Down (_)", 6: "Up/Down (L)", 7: "Up/Down (L&H)", 8: "Up/Down (_)", 9: "Random (L)", 10: "Random (_)", 11: "Phrase", } return names.get(self.value, "???")
@property
[docs] def midi_value(self) -> int: """Get MIDI value for grid""" return self.value
[docs] class ArpeggioGrid(Enum): """Arpeggiator grid values"""
[docs] QUARTER = 0 # 1/4
[docs] EIGHTH = 1 # 1/8
[docs] EIGHTH_T = 2 # 1/8T
[docs] SIXTEENTH = 3 # 1/16
[docs] SIXTEENTH_T = 4 # 1/16T
[docs] THIRTY_TWO = 5 # 1/32
[docs] THIRTY_TWO_T = 6 # 1/32T
@property
[docs] def display_name(self) -> str: """Get display name for grid value""" names = { 0: "1/4", 1: "1/8", 2: "1/8 Triplet", 3: "1/16", 4: "1/16 Triplet", 5: "1/32", 6: "1/32 Triplet", } return names.get(self.value, "???")
@property
[docs] def midi_value(self) -> int: """Get MIDI value for grid""" return self.value
[docs] class ArpeggioDuration(Enum): """Arpeggiator duration values"""
[docs] D30 = 0 # 30%
[docs] D40 = 1 # 40%
[docs] D50 = 2 # 50%
[docs] D60 = 3 # 60%
[docs] D70 = 4 # 70%
[docs] D80 = 5 # 80%
[docs] D90 = 6 # 90%
[docs] D100 = 7 # 100%
[docs] D120 = 8 # 120%
[docs] FUL = 9 # FULL
@property
[docs] def display_name(self) -> str: """Get display name for duration value""" names = { 0: "30%", 1: "40%", 2: "50%", 3: "60%", 4: "70%", 5: "80%", 6: "90%", 7: "100%", 8: "120%", 9: "Full", } return names.get(self.value, "???")
@property
[docs] def midi_value(self) -> int: """Get MIDI value for duration""" return self.value
[docs] class ArpeggioOctaveRange(Enum): """Arpeggio octave range values"""
[docs] OCT_MINUS_3 = -3
[docs] OCT_MINUS_2 = -2
[docs] OCT_MINUS_1 = -1
[docs] OCT_ZERO = 0
[docs] OCT_PLUS_1 = 1
[docs] OCT_PLUS_2 = 2
[docs] OCT_PLUS_3 = 3
@property
[docs] def display_name(self) -> str: """Get display name for octave range""" if self.value == 0: return "0" elif self.value > 0: return f"+{self.value}" else: return str(self.value)
@property
[docs] def midi_value(self) -> int: """Get MIDI value for octave range (centered at 64)""" return self.value + 64
[docs] class ArpeggioSwitch(Enum): """Arpeggiator switch values"""
[docs] OFF = 0
[docs] ON = 1
@property
[docs] def display_name(self) -> str: """Get display name for switch value""" return "ON" if self.value else "OFF"
@property
[docs] def midi_value(self) -> int: """Get MIDI value for switch""" return self.value