1+ """
2+ /*
3+ * ----------------------------------------------------------------------------
4+ * "THE BEER-WARE LICENSE" (Revision 42 modified):
5+ * <[email protected] > wrote this file. As long as you retain this notice and 6+ * my credit somewhere you can do whatever you want with this stuff. If we
7+ * meet some day, and you think this stuff is worth it, you can buy me a beer
8+ * in return.
9+ * ----------------------------------------------------------------------------
10+ */
11+ """
12+
13+ from machine import Pin , I2S
14+ import time
15+
16+ _PERIODS = ( # c0 thru a0 - how much to advance a sample pointer per frame for each note
17+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ,
18+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 ' ,
19+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 ' ,
20+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 ' ,
21+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x00 ' ,
22+ b'\x01 \x00 \x00 \x00 \x00 \x00 ' ,
23+ b'\x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 ' ,
24+ b'\x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 ' ,
25+ b'\x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 ' ,
26+ b'\x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 ' ,
27+ b'\x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 ' ,
28+ b'\x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x01 \x00 \x00 \x00 \x00 '
29+ )
30+
31+ _MIN_VOLUME = const (0 )
32+ _MAX_VOLUME = const (15 )
33+ @micropython .native
34+ def _volume (volume ):
35+ return 15 - (0 if volume < 0 else 15 if volume > 15 else volume )
36+
37+ @micropython .viper
38+ def _vipmod (a :uint , b :uint ) -> uint :
39+ while a >= b :
40+ a -= b
41+ return a
42+
43+ class Register :
44+ def __init__ (self , buf_start = 0 , sample = None , sample_len = 0 , pointer = 0 , note = 0 , period = 1 , period_mult = 4 , loop = False , volume = 0 ):
45+ self .buf_start = buf_start
46+ self .sample = sample
47+ self .pointer = pointer
48+ self .period = period
49+ self .note = note
50+ self .period_mult = period_mult
51+ self .sample_len = sample_len
52+ self .loop = loop
53+ self .volume = volume
54+
55+ def copy (self ):
56+ registers = Register ()
57+ registers .buf_start = self .buf_start
58+ registers .sample = self .sample
59+ registers .pointer = self .pointer
60+ registers .period = self .period
61+ registers .note = self .note
62+ registers .period_mult = self .period_mult
63+ registers .sample_len = self .sample_len
64+ registers .loop = self .loop
65+ registers .volume = self .volume
66+ return registers
67+
68+ def __str__ (self ):
69+ return f"{ self .buf_start } : { self .sample } v:{ self .volume } n:{ self .note } "
70+
71+ class M5Sound :
72+ def __init__ (self , buf_size = 2048 , rate = 11025 , channels = 4 , sck = 41 , ws = 43 , sd = 42 ):
73+ self ._output = I2S (
74+ 1 ,
75+ sck = Pin (sck ),
76+ ws = Pin (ws ),
77+ sd = Pin (sd ),
78+ mode = I2S .TX ,
79+ bits = 16 ,
80+ format = I2S .STEREO ,
81+ rate = rate ,
82+ ibuf = buf_size
83+ )
84+
85+ self ._rate = rate
86+ self ._buf_size :int = buf_size
87+ self ._buffer = bytearray (buf_size * 2 ) # twice for stereo
88+ self .channels = channels
89+ self ._registers = [Register () for _ in range (channels )]
90+ self ._queues = [[] for _ in range (channels )]
91+ self ._last_tick = 0
92+ self ._output .irq (self ._process_buffer )
93+ self ._process_buffer (None )
94+
95+ def __del__ (self ):
96+ self ._output .deinit ()
97+
98+ # @micropython.native
99+ def _gen_buf_start (self ):
100+ return int ((time .ticks_diff (time .ticks_us (), self ._last_tick ) // (1000000 / self ._rate )) * 2 ) # stereo
101+
102+ # @micropython.native
103+ def play (self , sample , note = 0 , octave = 4 , volume = 15 , channel = 0 , loop = False ):
104+ registers = Register (
105+ buf_start = self ._gen_buf_start (),
106+ sample = sample ,
107+ sample_len = len (sample ) // 2 ,
108+ loop = loop ,
109+ note = note % 12 ,
110+ period_mult = 2 ** (octave + (note // 12 )),
111+ volume = volume
112+ )
113+ self ._queues [channel ].append (registers )
114+
115+ # @micropython.native
116+ def stop (self , channel = 0 ):
117+ registers = Register () # default has empty sample
118+ registers .buf_start = self ._gen_buf_start ()
119+ self ._queues [channel ].append (registers )
120+
121+ # @micropython.native
122+ def setvolume (self , volume , channel = 0 ):
123+ if len (self ._queues [channel ]) > 0 :
124+ registers = self ._queues [channel ][- 1 ].copy ()
125+ else :
126+ registers = self ._registers [channel ].copy ()
127+ registers .buf_start = self ._gen_buf_start ()
128+ registers .volume = volume
129+ self ._queues [channel ].append (registers )
130+
131+ @micropython .viper
132+ def _clear_buffer (self ):
133+ buf = ptr16 (self ._buffer )
134+ for i in range (0 , int (self ._buf_size )):
135+ buf [i ] = 0
136+
137+ @micropython .viper
138+ def _fill_buffer (self , registers , end :int ) -> bool :
139+ buf = ptr16 (self ._buffer )
140+ start = 1 + int (registers .buf_start )
141+ smp = ptr16 (registers .sample )
142+ slen = uint (registers .sample_len )
143+ ptr = uint (registers .pointer )
144+ per = ptr8 (_PERIODS [registers .note ])
145+ perlen = uint (len (_PERIODS [registers .note ]))
146+ perptr = uint (registers .period )
147+ permult = int (registers .period_mult )
148+ vol = uint (_volume (registers .volume ))
149+ loop = bool (registers .loop )
150+ for i in range (start , end , 2 ): # odd word: right channel only
151+ if ptr >= slen : # sample ended
152+ if not loop : # stop playing
153+ return False
154+ ptr = uint (_vipmod (ptr , slen )) # or loop
155+ bsmp = smp [ptr ]
156+ # ladies and gentlemen, the two's complement
157+ bsmp = (bsmp & 0b1000000000000000 ) | ((bsmp & 0b0111111111111111 ) >> vol )
158+ if (bsmp & 0b1000000000000000 ) != 0 :
159+ bsmp |= (0b111111111111111 << 15 - vol )
160+ buf [i ] += bsmp
161+ for _ in range (permult ): # add together frame periods for different octaves
162+ ptr += per [perptr ]
163+ perptr += uint (1 )
164+ if perptr >= perlen :
165+ perptr = uint (0 )
166+ registers .buf_start = 0
167+ registers .pointer = ptr
168+ registers .period = perptr
169+ return True
170+
171+ # @micropython.native
172+ def _process_buffer (self , arg ):
173+ self ._last_tick = time .ticks_us ()
174+ self ._output .write (self ._buffer )
175+ self ._clear_buffer ()
176+
177+ for ch in range (0 , int (self .channels )):
178+ playing = True
179+ while playing :
180+ registers = self ._registers [ch ]
181+
182+ end = self ._buf_size
183+ if len (self ._queues [ch ]) > 0 :
184+ if self ._queues [ch ][0 ].buf_start >= self ._buf_size :
185+ self ._queues [ch ][0 ].buf_start -= self ._buf_size
186+ else :
187+ end = self ._queues [ch ][0 ].buf_start
188+ self ._registers [ch ] = self ._queues [ch ].pop (0 )
189+ else :
190+ playing = False
191+
192+ if registers .sample :
193+ if not self ._fill_buffer (registers , end ):
194+ registers .sample = None
0 commit comments