git.s-ol.nu hw/0x33.board/firmware / 0ef957f
automatic formatting black -S s-ol 1 year, 3 days ago
8 changed file(s) with 220 addition(s) and 91 deletion(s). Raw diff Collapse all Expand all
1616 from .matrix import Matrix
1717 from .scale import Scale
1818 from .layout import Layout, WickiHaydenLayout, HarmonicLayout, GerhardLayout
19 from .menu import Settings, SliderSetting, ChoiceSetting, MenuMode, _eq, _thresh_offor, _color_offor
19 from .menu import (
20 Settings,
21 SliderSetting,
22 ChoiceSetting,
23 MenuMode,
24 _eq,
25 _thresh_offor,
26 _color_offor,
27 )
2028 from .base import BaseMode, BaseShiftMode
2129
2230
4856 displayio.release_displays()
4957 i2c = I2C(sda=board.GP14, scl=board.GP15, frequency=1000000)
5058 bus = I2CDisplay(i2c, device_address=0x3C)
51 self.display = SSD1306(bus, width=128, height=32, rotation=180, auto_refresh=False)
59 self.display = SSD1306(
60 bus, width=128, height=32, rotation=180, auto_refresh=False
61 )
5262 self.display.show(displayio.Group())
5363 self.display.refresh()
5464
6272 self.audio_out = PWMAudioOut(left_channel=board.GP12, right_channel=board.GP13)
6373 self.test_file = WaveFile(open("test.wav", "rb"))
6474
65 self.settings = Settings({
66 "active_profile": SliderSetting("CURRENT PROFILE", 24, fmt="Profile {}", thresh=_eq),
67 "midi_ch_usb": SliderSetting("USB MIDI CHANNEL", 15, fmt="CH{}", thresh=_eq),
68 "midi_ch_trs": SliderSetting("TRS MIDI CHANNEL", 15, fmt="CH{}", thresh=_eq),
69 "midi_vel": SliderSetting("MIDI VELOCITY", 127, default=64),
70 "rgb_bright": SliderSetting("LED BRIGHTNESS", 100, default=100, fmt="{}%", color=_color_offor, thresh=_thresh_offor),
71 "jam_timeout": ChoiceSetting("JAM MODE FADE TIME", [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 10, 12, 20], default=6, fmt="{:.2f}s", color=_color_offor),
72 "layout_name": ChoiceSetting("KEYBOARD LAYOUT", ['wicki/hayden', 'harmonic table', 'gerhard'], default='wicki/hayden'),
73 "layout_offset": SliderSetting("LAYOUT START NOTE", 127, default=24),
74 "scale_name": ChoiceSetting("HIGHLIGHT SCALE", ['major', 'min nat', 'min harm', 'min mel', 'min hung', 'whole', 'penta'], default='major'),
75 "scale_root": SliderSetting("SCALE ROOT NOTE", 127, default=43),
76 })
75 self.settings = Settings(
76 {
77 "active_profile": SliderSetting(
78 "CURRENT PROFILE", 24, fmt="Profile {}", thresh=_eq
79 ),
80 "midi_ch_usb": SliderSetting(
81 "USB MIDI CHANNEL", 15, fmt="CH{}", thresh=_eq
82 ),
83 "midi_ch_trs": SliderSetting(
84 "TRS MIDI CHANNEL", 15, fmt="CH{}", thresh=_eq
85 ),
86 "midi_vel": SliderSetting("MIDI VELOCITY", 127, default=64),
87 "rgb_bright": SliderSetting(
88 "LED BRIGHTNESS",
89 100,
90 default=100,
91 fmt="{}%",
92 color=_color_offor,
93 thresh=_thresh_offor,
94 ),
95 "jam_timeout": ChoiceSetting(
96 "JAM MODE FADE TIME",
97 [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 10, 12, 20],
98 default=6,
99 fmt="{:.2f}s",
100 color=_color_offor,
101 ),
102 "layout_name": ChoiceSetting(
103 "KEYBOARD LAYOUT",
104 ['wicki/hayden', 'harmonic table', 'gerhard'],
105 default='wicki/hayden',
106 ),
107 "layout_offset": SliderSetting("LAYOUT START NOTE", 127, default=24),
108 "scale_name": ChoiceSetting(
109 "HIGHLIGHT SCALE",
110 [
111 'major',
112 'min nat',
113 'min harm',
114 'min mel',
115 'min hung',
116 'whole',
117 'penta',
118 ],
119 default='major',
120 ),
121 "scale_root": SliderSetting("SCALE ROOT NOTE", 127, default=43),
122 }
123 )
77124
78125 self.layout = WickiHaydenLayout()
79126 self.scale = Scale(0, Scale.STEPS["major"], "major")
93140 self.modes = {
94141 "base": BaseMode(self),
95142 "base_shift": BaseShiftMode(self),
96 "menu": MenuMode(self, settings=self.settings, ui_settings_ids=[
97 "active_profile", "midi_ch_usb", "midi_ch_trs", "midi_vel", "rgb_bright", "jam_timeout", "layout_name"
98 ]),
143 "menu": MenuMode(
144 self,
145 settings=self.settings,
146 ui_settings_ids=[
147 "active_profile",
148 "midi_ch_usb",
149 "midi_ch_trs",
150 "midi_vel",
151 "rgb_bright",
152 "jam_timeout",
153 "layout_name",
154 ],
155 ),
99156 }
100157 self.mode = self.modes["base"]
101158
102159 self.settings.load()
103160 self.mode.update_scale()
104161
105 def on_midi_ch_usb(self, ch): self.midi_usb.out_channel = ch
106 def on_midi_ch_trs(self, ch): self.midi_trs.out_channel = ch
107 def on_midi_vel(self, vel): self.velocity = vel
108 def on_rgb_bright(self, b): self.pixels.brightness = b/100
109 def on_jam_timeout(self, t): self.jam_timeout = t * 1000
162 def on_midi_ch_usb(self, ch):
163 self.midi_usb.out_channel = ch
164
165 def on_midi_ch_trs(self, ch):
166 self.midi_trs.out_channel = ch
167
168 def on_midi_vel(self, vel):
169 self.velocity = vel
170
171 def on_rgb_bright(self, b):
172 self.pixels.brightness = b / 100
173
174 def on_jam_timeout(self, t):
175 self.jam_timeout = t * 1000
110176
111177 def on_layout(self, v):
112178 name = self.settings.get('layout_name').value
2727
2828 self._notes_dirty = False
2929
30 self.scale_label = label.Label(FONT_10, text="", color=0xffffff, anchor_point=(0, 0), anchored_position=(0, 0))
31 self.notes_label = label.Label(FONT_10, text="", color=0xffffff, anchor_point=(0, 0), anchored_position=(10, 12))
30 self.scale_label = label.Label(
31 FONT_10,
32 text="",
33 color=0xFFFFFF,
34 anchor_point=(0, 0),
35 anchored_position=(0, 0),
36 )
37 self.notes_label = label.Label(
38 FONT_10,
39 text="",
40 color=0xFFFFFF,
41 anchor_point=(0, 0),
42 anchored_position=(10, 12),
43 )
3244 self.group.append(self.scale_label)
3345 self.group.append(self.notes_label)
3446
35 self.group.append(label.Label(
36 FONT_10,
37 text="OCTVE",
38 color=0xffffff,
39 anchor_point=(1, 0.5),
40 anchored_position=(128, 16),
41 label_direction="DWR",
42 ))
43 self.group.append(rect.Rect(
44 x=118,
45 y=0,
46 width=1,
47 height=32,
48 fill=0xffffff,
49 ))
47 self.group.append(
48 label.Label(
49 FONT_10,
50 text="OCTVE",
51 color=0xFFFFFF,
52 anchor_point=(1, 0.5),
53 anchored_position=(128, 16),
54 label_direction="DWR",
55 )
56 )
57 self.group.append(
58 rect.Rect(
59 x=118,
60 y=0,
61 width=1,
62 height=32,
63 fill=0xFFFFFF,
64 )
65 )
5066
5167 def update_scale(self):
5268 for key in self.keys:
6884 if self._notes_dirty:
6985 active = [pitch for pitch in self.notes]
7086 active.sort()
71 self.notes_label.text = ' '.join(self.keyboard.scale.label(p) for p in active)
87 self.notes_label.text = ' '.join(
88 self.keyboard.scale.label(p) for p in active
89 )
7290 self._notes_dirty = False
7391
7492 def key_event(self, i: int, pressed: bool) -> bool:
115133 def __init__(self, *args):
116134 super().__init__(*args)
117135
118 self.scale_label = label.Label(FONT_10, text="", color=0xffffff, anchor_point=(0, 0), anchored_position=(0, 0))
136 self.scale_label = label.Label(
137 FONT_10,
138 text="",
139 color=0xFFFFFF,
140 anchor_point=(0, 0),
141 anchored_position=(0, 0),
142 )
119143 self.group.append(self.scale_label)
120144
121 self.group.append(label.Label(FONT_10, text="choose root note", color=0xffffff, anchor_point=(0, 0), anchored_position=(10, 12)))
122
123 self.group.append(label.Label(
124 FONT_10,
125 text="SCALE",
126 color=0xffffff,
127 anchor_point=(1, 0.5),
128 anchored_position=(128, 16),
129 label_direction="DWR",
130 ))
131 self.group.append(rect.Rect(
132 x=118,
133 y=0,
134 width=1,
135 height=32,
136 fill=0xffffff,
137 ))
145 self.group.append(
146 label.Label(
147 FONT_10,
148 text="choose root note",
149 color=0xFFFFFF,
150 anchor_point=(0, 0),
151 anchored_position=(10, 12),
152 )
153 )
154
155 self.group.append(
156 label.Label(
157 FONT_10,
158 text="SCALE",
159 color=0xFFFFFF,
160 anchor_point=(1, 0.5),
161 anchored_position=(128, 16),
162 label_direction="DWR",
163 )
164 )
165 self.group.append(
166 rect.Rect(
167 x=118,
168 y=0,
169 width=1,
170 height=32,
171 fill=0xFFFFFF,
172 )
173 )
138174
139175 def enter(self):
140176 self.entered = supervisor.ticks_ms()
5555
5656
5757 class Key:
58 MENU_I = const(48+0)
59 PREV_I = const(48+4)
60 NEXT_I = const(48+5)
58 MENU_I = const(48 + 0)
59 PREV_I = const(48 + 4)
60 NEXT_I = const(48 + 5)
6161
6262 keyboard: Keyboard
6363
00 import time
1
12 times = []
23 names = []
34
1516
1617 def stop():
1718 timestr = ' + '.join(
18 f"{(times[i+1] - times[i]) // 1000000}ms {text}"
19 for i, text in enumerate(names)
19 f"{(times[i+1] - times[i]) // 1000000}ms {text}" for i, text in enumerate(names)
2020 )
2121 print(f"{timestr} = {(times[-1] - times[0]) // 1000000}ms")
1515 class WickiHaydenLayout(Layout):
1616 def get_pitch(self, key: Key) -> int:
1717 x, y = key.pos
18 return int(self.offset + 2*x + 6*y)
18 return int(self.offset + 2 * x + 6 * y)
1919
2020
2121 class HarmonicLayout(Layout):
2222 def get_pitch(self, key: Key) -> int:
2323 x, y = key.pos
24 return int(self.offset + 4*x + 5*y)
24 return int(self.offset + 4 * x + 5 * y)
2525
2626
2727 class GerhardLayout(Layout):
2828 def get_pitch(self, key: Key) -> int:
2929 x, y = key.pos
30 return int(self.offset + 3*x + 2.5*y)
30 return int(self.offset + 3 * x + 2.5 * y)
6666 _value: int
6767 fmt: str | None
6868
69 def __init__(self, name: str, max: int, default: int = 0, fmt = None, thresh = _thresh, color = _color):
69 def __init__(
70 self,
71 name: str,
72 max: int,
73 default: int = 0,
74 fmt=None,
75 thresh=_thresh,
76 color=_color,
77 ):
7078 super().__init__(name, default)
7179
7280 self.max = max
106114 class ChoiceSetting(SliderSetting):
107115 def __init__(self, name: str, values: list, default=0, **kwargs):
108116 default = values.index(default)
109 super().__init__(name, len(values) - 1, default=default, thresh=lambda v, t: v == t, **kwargs)
117 super().__init__(
118 name, len(values) - 1, default=default, thresh=lambda v, t: v == t, **kwargs
119 )
110120 self.values = values
111121
112122 @property
195205 self.settings_i = 0
196206
197207 self.group = displayio.Group()
198 self.label_settings = label.Label(FONT_10, text="volume", color=0xffffff, anchor_point=(0, 0), anchored_position=(0, 0))
199 self.label_value = label.Label(FONT_10, text="100%", color=0xffffff, anchor_point=(1, 0), anchored_position=(110, 12))
208 self.label_settings = label.Label(
209 FONT_10,
210 text="volume",
211 color=0xFFFFFF,
212 anchor_point=(0, 0),
213 anchored_position=(0, 0),
214 )
215 self.label_value = label.Label(
216 FONT_10,
217 text="100%",
218 color=0xFFFFFF,
219 anchor_point=(1, 0),
220 anchored_position=(110, 12),
221 )
200222 self.group.append(self.label_settings)
201223 self.group.append(self.label_value)
202224
203 self.group.append(label.Label(
204 FONT_10,
205 text="VALUE",
206 color=0xffffff,
207 anchor_point=(1, 0.5),
208 anchored_position=(128, 16),
209 label_direction="DWR",
210 ))
211 self.group.append(rect.Rect(
212 x=118,
213 y=0,
214 width=1,
215 height=32,
216 fill=0xffffff,
217 ))
225 self.group.append(
226 label.Label(
227 FONT_10,
228 text="VALUE",
229 color=0xFFFFFF,
230 anchor_point=(1, 0.5),
231 anchored_position=(128, 16),
232 label_direction="DWR",
233 )
234 )
235 self.group.append(
236 rect.Rect(
237 x=118,
238 y=0,
239 width=1,
240 height=32,
241 fill=0xFFFFFF,
242 )
243 )
218244
219245 def enter(self):
220246 self.update_display()
244270 pixels[led_map[self.settings_i]] = self.SETTING_ACTIVE
245271
246272 for i, color in enumerate(self.setting.get_colors()):
247 pixels[led_map[24+i]] = color
273 pixels[led_map[24 + i]] = color
248274
249275 def update_display(self):
250276 self.label_settings.text = self.setting.name + ':'
3434
3535 if relative_pitch not in self.notes:
3636 # out of scale
37 return (.3, 0.8, 0.0)
37 return (0.3, 0.8, 0.0)
3838
3939 if self.root <= pitch < self.root + self.size:
4040 # in core scale
41 return (.1, 1.0, 0.2)
41 return (0.1, 1.0, 0.2)
4242
4343 # in scale
44 return (.7, 0.9, 0.15)
44 return (0.7, 0.9, 0.15)
4545
4646 def label(self, pitch):
4747 return self.LABELS[pitch % self.size]
1010
1111
1212 def ticks_diff(ticks1, ticks2):
13 ''' Compute the signed difference between two ticks values,
14 assuming that they are within 2**28 ticks
13 '''
14 Compute the signed difference between two ticks values,
15 assuming that they are within 2**28 ticks
1516 '''
1617
1718 diff = (ticks1 - ticks2) & _TICKS_MAX