Skip to content

Commit b8c3316

Browse files
committed
feature/unit/MIDI sound systems, built-in SAM2695 audio synthesizer
1 parent 77d7269 commit b8c3316

File tree

3 files changed

+317
-0
lines changed

3 files changed

+317
-0
lines changed

m5stack/libs/unit/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@
4343
"DAC2Unit": "dac2",
4444
"GESTUREUnit": "gesture",
4545
"THERMALUnit": "thermal",
46+
"SYNTHUnit": "synth",
47+
"SERVOS8Unit": "servos8",
48+
"RTC8563Unit": "rtc8563",
49+
"VMeterUnit": "vmeter",
50+
"AMeterUnit": "ameter",
4651
}
4752

4853

m5stack/libs/unit/manifest.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
"scales.py",
4646
"gesture.py",
4747
"thermal.py",
48+
"synth.py",
49+
"servos8.py",
50+
"rtc8563.py",
51+
"vmeter.py",
52+
"ameter.py",
4853
),
4954
base_path="..",
5055
opt=0,

m5stack/libs/unit/synth.py

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
'''
2+
Refer to https://github.com/fluxly/Fluxamasynth.
3+
'''
4+
5+
6+
from machine import UART, Pin
7+
8+
9+
MIDI_CMD_NOTE_OFF = 0x80 # Note Off
10+
MIDI_CMD_NOTE_ON = 0x90 # Note On
11+
MIDI_CMD_POLYPHONIC_AFTERTOUCH = 0xA0 # Polyphonic Aftertouch (or Key Pressure)
12+
MIDI_CMD_CONTROL_CHANGE = 0xB0 # Control Change (or Channel Mode Message)
13+
MIDI_CMD_PROGRAM_CHANGE = 0xC0 # Program Change
14+
MIDI_CMD_CHANNEL_AFTERTOUCH = 0xD0 # Channel Aftertouch (or Channel Pressure)
15+
MIDI_CMD_PITCH_BEND = 0xE0 # Pitch Bend
16+
MIDI_CMD_SYSTEM_EXCLUSIVE = 0xF0 # System Exclusive (SysEx) Start
17+
MIDI_CMD_TIME_CODE = 0xF1 # MIDI Time Code Quarter Frame
18+
MIDI_CMD_SONG_POSITION = 0xF2 # Song Position Pointer
19+
MIDI_CMD_SONG_SELECT = 0xF3 # Song Select
20+
MIDI_CMD_TUNE_REQUEST = 0xF6 # Tune Request
21+
MIDI_CMD_END_OF_SYSEX = 0xF7 # End of SysEx
22+
MIDI_CMD_TIMING_CLOCK = 0xF8 # Timing Clock (used in System Real-Time Messages)
23+
MIDI_CMD_START = 0xFA # Start (used in System Real-Time Messages)
24+
MIDI_CMD_CONTINUE = 0xFB # Continue (used in System Real-Time Messages)
25+
MIDI_CMD_STOP = 0xFC # Stop (used in System Real-Time Messages)
26+
MIDI_CMD_ACTIVE_SENSING = 0xFE # Active Sensing (used in System Real-Time Messages)
27+
MIDI_CMD_SYSTEM_RESET = 0xFF # System Reset
28+
29+
DRUM_SET ={ "Closed Hi Hat [EXC1]" :[27, 49],
30+
"Pedal Hi-Hat [EXC1]" :[28, 49],
31+
"Open Hi Hat [EXC1]" :[29, 49],
32+
"Ride Cymbal" :[30, 49],
33+
"Kick drum2" :[35, 1],
34+
"Jazz BD 2" :[35, 41],
35+
"Kick drum" :[35, 128],
36+
"Kick drum1" :[36, 1],
37+
"Jazz BD 1" :[36, 41],
38+
# "Kick drum" :[36, 128],
39+
"Side Stick" :[37, 1],
40+
"Rim Shot" :[37, 128],
41+
"Snare Drum 1" :[38, 1],
42+
"Gated Snare" :[38, 17],
43+
"Brush Tap" :[38, 41],
44+
"Snare Drum 2" :[38, 49],
45+
"Snare Drum" :[38, 128],
46+
"Hand Clap" :[39, 1],
47+
"Brush Slap" :[39, 41],
48+
"Castanets" :[39, 49],
49+
"Hand Clap" :[39, 128],
50+
"Snare Drum 2" :[40, 1],
51+
"Brush Swirl" :[40, 41],
52+
"Snare Drum 2" :[40, 49],
53+
"Elec Snare Drum" :[40, 128],
54+
"Low Floor Tom" :[41, 1],
55+
"Timpani F" :[41, 49],
56+
"Acoustic Low Tom" :[41, 128],
57+
"Closed Hi Hat [EXC1]" :[42, 1],
58+
"Timpani F#" :[42, 49],
59+
"Closed Hi-Hat [Exc1]" :[42, 128],
60+
"High Floor Tom" :[43, 49],
61+
"Timpani G" :[43, 49],
62+
"Acoustic Low Tom" :[43, 128],
63+
"Pedal Hi-Hat [EXC1]" :[44, 1],
64+
"Timpani G#" :[44, 49],
65+
"Open Hi-Hat 2" :[44, 128],
66+
"Low Tom" :[45, 1],
67+
"Timpani A" :[45, 49],
68+
"Acoustic Middle Tom" :[45, 128],
69+
"Open Hi-Hat [EXC1]" :[46, 1],
70+
"Timpani A#" :[46, 49],
71+
"Open Hi-Hat 1 [Exc1]" :[46, 128],
72+
"Low-Mid Tom" :[47, 1],
73+
"Timpani B" :[47, 49],
74+
"Acoustic Middle Tom" :[47, 128],
75+
"Hi Mid Tom" :[48, 1],
76+
"Timpani c" :[48, 49],
77+
"Acoustic High Tom" :[48, 128],
78+
"Crash Cymbal 1" :[49, 1],
79+
"Timpani c#" :[49, 49],
80+
"Crash Cymbal" :[49, 128],
81+
"High Tom" :[50, 1],
82+
"Timpani d" :[50, 49],
83+
"Acoustic High Tom" :[50, 128],
84+
"Ride Cymbal 1" :[51, 1],
85+
"Timpani d#" :[51, 49],
86+
"Ride Cymbal" :[51, 128],
87+
"Chinese Cymbal" :[52, 1],
88+
"Timpani e" :[52, 49],
89+
"Ride Bell" :[53, 1],
90+
"Timpani f" :[53, 49],
91+
"Tambourine" :[54, 1],
92+
# "Tambourine" :[54, 128],
93+
"Splash Cymbal" :[55, 1],
94+
"Cowbell" :[56, 1],
95+
# "Cowbell" :[56, 128],
96+
"Crash Cymbal 2" :[57, 1],
97+
"Vibraslap" :[58, 1],
98+
"Ride Cymbal 2" :[59, 1],
99+
"Hi Bongo" :[60, 1],
100+
"Low Bongo" :[61, 1],
101+
"Mute Hi Conga" :[62, 1],
102+
"Open Hi Conga" :[63, 1],
103+
"Low Conga" :[64, 1],
104+
"High Timbale" :[65, 1],
105+
"Low Timbale" :[66, 1],
106+
"High Agogo" :[67, 1],
107+
"Low Agogo" :[68, 1],
108+
"Cabasa" :[69, 1],
109+
"Maracas" :[70, 1],
110+
"Short Whistle[EXC2]" :[71, 1],
111+
"Long Whistle[EXC2]" :[72, 1],
112+
"Short Guiro [EXC3]" :[73, 1],
113+
"Vibra Slap" :[73, 128],
114+
"Long Guiro [EXC3]" :[74, 1],
115+
"Claves" :[75, 1],
116+
# "Claves" :[75, 128],
117+
"Hi Wood Block" :[76, 1],
118+
"Low Wood Block" :[77, 1],
119+
"Mute Cuica [EXC4]" :[78, 1],
120+
"Open Cuica [EXC4]" :[79, 1],
121+
"Mute Triangle [EXC5]" :[80, 1],
122+
"Open Triangle[EXC5]" :[81, 1],
123+
"Applauses" :[82, 128],
124+
# "Applauses" :[88, 49],
125+
"Helicopter" :[94, 127],
126+
"Gun Shot" :[96, 127],
127+
"Birds" :[102, 127],
128+
"SeaShore" :[106, 127],
129+
}
130+
131+
class SYNTHUnit:
132+
133+
def __init__(self, port, port_id=1):
134+
Pinx = Pin(port[0], Pin.IN, Pin.PULL_UP)
135+
self._uart = UART(port_id, tx=port[1], rx=port[0])
136+
self._uart.init(31250, bits=8, parity=None, stop=1)
137+
138+
def set_note_on(self, channel, pitch, velocity):
139+
cmd = [MIDI_CMD_NOTE_ON | (channel & 0x0f), pitch, velocity]
140+
self.cmd_write(cmd)
141+
142+
def set_note_off(self, channel, pitch):
143+
cmd = [MIDI_CMD_NOTE_OFF | (channel & 0x0f), pitch, 0x00]
144+
self.cmd_write(cmd)
145+
146+
def set_instrument(self, bank, channel, value):
147+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x00, bank]
148+
self.cmd_write(cmd)
149+
cmd = [MIDI_CMD_PROGRAM_CHANGE | (channel & 0x0f), value]
150+
self.cmd_write(cmd)
151+
152+
def set_drums_instrument(self, drum_pitch, velocity):
153+
self.set_instrument(0, 9, DRUM_SET[drum_pitch][1])
154+
self.set_note_on(9, DRUM_SET[drum_pitch][0], velocity)
155+
156+
def set_pitch_bend(self, channel, value):
157+
value = self.map(value, 0, 1023, 0, 0x3fff)
158+
cmd = [MIDI_CMD_PITCH_BEND | (channel & 0x0f), (value & 0xef), ((value >> 7) & 0xff)]
159+
self.cmd_write(cmd)
160+
161+
def set_pitch_bend_range(self, channel, value):
162+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x65, 0x00, 0x64, 0x00, 0x06, (value & 0x7f)]
163+
self.cmd_write(cmd)
164+
165+
def midi_reset(self):
166+
self.cmd_write([MIDI_CMD_SYSTEM_RESET])
167+
168+
def set_channel_volume(self, channel, level):
169+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x07, level]
170+
self.cmd_write(cmd)
171+
172+
def set_all_notes_off(self, channel):
173+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x7b, 0x00]
174+
self.cmd_write(cmd)
175+
176+
def set_master_volume(self, level):
177+
cmd = [MIDI_CMD_SYSTEM_EXCLUSIVE, 0x7f, 0x7f, 0x04, 0x01, 0x00, (level & 0x7f), MIDI_CMD_END_OF_SYSEX]
178+
self.cmd_write(cmd)
179+
180+
def set_reverb(self, channel, program, level, delayfeedback):
181+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x50, (program & 0x07)]
182+
self.cmd_write(cmd)
183+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x5b, (level & 0x7f)]
184+
self.cmd_write(cmd)
185+
if delayfeedback > 0:
186+
cmd=[MIDI_CMD_SYSTEM_EXCLUSIVE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x01, 0x35, (delayfeedback & 0x7f), 0x00, MIDI_CMD_END_OF_SYSEX]
187+
self.cmd_write(cmd)
188+
189+
def set_chorus(self, channel, program, level, feedback, chorusdelay):
190+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x51, (program & 0x07)]
191+
self.cmd_write(cmd)
192+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x5d, (level & 0x7f)]
193+
self.cmd_write(cmd)
194+
if feedback > 0:
195+
cmd=[MIDI_CMD_SYSTEM_EXCLUSIVE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x01, 0x3b, (feedback & 0x7f), 0x00, MIDI_CMD_END_OF_SYSEX]
196+
self.cmd_write(cmd)
197+
if chorusdelay > 0:
198+
cmd=[MIDI_CMD_SYSTEM_EXCLUSIVE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x01, 0x3c, (feedback & 0x7f), 0x00, MIDI_CMD_END_OF_SYSEX]
199+
self.cmd_write(cmd)
200+
201+
def set_pan(self, channel, value):
202+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x0A, value]
203+
self.cmd_write(cmd)
204+
205+
def set_equalizer(self, channel, lowband, medlowband, medhighband, highband,
206+
lowfreq, medlowfreq, medhighfreq, highfreq):
207+
208+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x63, 0x37, 0x62, 0x00, 0x06, (lowband & 0x7f)]
209+
self.cmd_write(cmd)
210+
cmd[4] = 0x01
211+
cmd[6] = (medlowband & 0x7f)
212+
self.cmd_write(cmd)
213+
cmd[4] = 0x02
214+
cmd[6] = (medhighband & 0x7f)
215+
self.cmd_write(cmd)
216+
cmd[4] = 0x03
217+
cmd[6] = (highband & 0x7f)
218+
self.cmd_write(cmd)
219+
cmd[4] = 0x08
220+
cmd[6] = (lowfreq & 0x7f)
221+
self.cmd_write(cmd)
222+
cmd[4] = 0x09
223+
cmd[6] = (medlowfreq & 0x7f)
224+
self.cmd_write(cmd)
225+
cmd[4] = 0x0A
226+
cmd[6] = (medhighfreq & 0x7f)
227+
self.cmd_write(cmd)
228+
cmd[4] = 0x0B
229+
cmd[6] = (highfreq & 0x7f)
230+
self.cmd_write(cmd)
231+
232+
def set_tuning(self, channel, fine, coarse):
233+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x65, 0x00, 0x64, 0x01, 0x06, (fine & 0x7f)]
234+
self.cmd_write(cmd)
235+
cmd[4] = 0x02
236+
cmd[6] = (coarse & 0x7f)
237+
self.cmd_write(cmd)
238+
239+
def set_vibrate(self, channel, rate, depth, delay):
240+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x63, 0x01, 0x62, 0x08, 0x06, (rate & 0x7f)]
241+
self.cmd_write(cmd)
242+
cmd[4] = 0x09
243+
cmd[6] = (depth & 0x7f)
244+
self.cmd_write(cmd)
245+
cmd[4] = 0x0A
246+
cmd[6] = (delay & 0x7f)
247+
self.cmd_write(cmd)
248+
249+
def set_tvf(self, channel, cutoff, resonance):
250+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x63, 0x01, 0x62, 0x20, 0x06, (cutoff & 0x7f)]
251+
self.cmd_write(cmd)
252+
cmd[4] = 0x21
253+
cmd[6] = (resonance & 0x7f)
254+
self.cmd_write(cmd)
255+
256+
def set_envelope(self, channel, attack, decay, release):
257+
cmd = [MIDI_CMD_CONTROL_CHANGE | (channel & 0x0f), 0x63, 0x01, 0x62, 0x63, 0x06, (attack & 0x7f)]
258+
self.cmd_write(cmd)
259+
cmd[4] = 0x64
260+
cmd[6] = (decay & 0x7f)
261+
self.cmd_write(cmd)
262+
cmd[4] = 0x66
263+
cmd[6] = (release & 0x7f)
264+
self.cmd_write(cmd)
265+
266+
def set_scale_tuning(self, channel, v1, v2, v3, v4, v5, v6,
267+
v7, v8, v9, v10, v11, v12):
268+
cmd = [MIDI_CMD_CONTROL_CHANGE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x10 | (channel & 0x0f), 0x40,
269+
v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, MIDI_CMD_END_OF_SYSEX]
270+
self.cmd_write(cmd)
271+
272+
def set_mod_wheel(self, channel, pitch, tvtcutoff, amplitude, rate, pitchdepth, tvfdepth, tvadepth):
273+
cmd = [MIDI_CMD_CONTROL_CHANGE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x20 | (channel & 0x0f), 0x00, pitch, 0x00, MIDI_CMD_END_OF_SYSEX]
274+
self.cmd_write(cmd)
275+
cmd[8] = 0x01
276+
cmd[9] = tvtcutoff
277+
self.cmd_write(cmd)
278+
cmd[8] = 0x02
279+
cmd[9] = amplitude
280+
self.cmd_write(cmd)
281+
cmd[8] = 0x03
282+
cmd[9] = rate
283+
self.cmd_write(cmd)
284+
cmd[8] = 0x04
285+
cmd[9] = pitchdepth
286+
self.cmd_write(cmd)
287+
cmd[8] = 0x05
288+
cmd[9] = tvfdepth
289+
self.cmd_write(cmd)
290+
cmd[8] = 0x06
291+
cmd[9] = tvadepth
292+
self.cmd_write(cmd)
293+
294+
def set_all_drums(self):
295+
cmd = [MIDI_CMD_CONTROL_CHANGE, 0x41, 0x00, 0x42, 0x12, 0x40, 0x10, 0x15, 0x01, 0x00, MIDI_CMD_END_OF_SYSEX]
296+
self.cmd_write(cmd)
297+
for i in range(1, 15):
298+
cmd[6] = i
299+
self.cmd_write(cmd)
300+
301+
def cmd_write(self, cmd):
302+
self._uart.write(bytes(cmd))
303+
304+
def map(self, x, in_min, in_max, out_min, out_max):
305+
return round((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min)
306+
307+

0 commit comments

Comments
 (0)