Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb2329aa

Browse files
AudioPlayer: support 8, 16, 24 and 32 bits per sample
1 parentd3be0ce commitb2329aa

File tree

1 file changed

+92
-30
lines changed
  • internal_filesystem/apps/com.micropythonos.musicplayer/assets

1 file changed

+92
-30
lines changed

‎internal_filesystem/apps/com.micropythonos.musicplayer/assets/audio_player.py‎

Lines changed: 92 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55

66

77
# ----------------------------------------------------------------------
8-
# AudioPlayer – robust, volume-controllable WAV player (MONO + STEREO)
9-
# Auto-up-samples any rate < 22050 Hz to 22050 Hz for MAX98357
8+
# AudioPlayer – robust, volume-controllable WAV player
9+
# Supports 8 / 16 / 24 / 32-bit PCM, mono + stereo
10+
# Auto-up-samples any rate < 22050 Hz to >=22050 Hz
1011
# ----------------------------------------------------------------------
1112
classAudioPlayer:
1213
_i2s=None
1314
_volume=50# 0-100
1415
_keep_running=True
1516

1617
# ------------------------------------------------------------------
17-
# WAV header parser
18+
# WAV header parser – returns bit-depth
1819
# ------------------------------------------------------------------
1920
@staticmethod
2021
deffind_data_chunk(f):
21-
"""Return (data_start, data_size, sample_rate, channels)"""
22+
"""Return (data_start, data_size, sample_rate, channels, bits_per_sample)"""
2223
f.seek(0)
2324
iff.read(4)!=b'RIFF':
2425
raiseValueError("Not a RIFF file")
@@ -29,6 +30,7 @@ def find_data_chunk(f):
2930
pos=12
3031
sample_rate=None
3132
channels=None
33+
bits_per_sample=None
3234
whilepos<file_size:
3335
f.seek(pos)
3436
chunk_id=f.read(4)
@@ -45,10 +47,11 @@ def find_data_chunk(f):
4547
ifchannelsnotin (1,2):
4648
raiseValueError("Only mono or stereo supported")
4749
sample_rate=int.from_bytes(fmt[4:8],'little')
48-
ifint.from_bytes(fmt[14:16],'little')!=16:
49-
raiseValueError("Only 16-bit supported")
50+
bits_per_sample=int.from_bytes(fmt[14:16],'little')
51+
ifbits_per_samplenotin (8,16,24,32):
52+
raiseValueError("Only 8/16/24/32-bit PCM supported")
5053
elifchunk_id==b'data':
51-
returnf.tell(),chunk_size,sample_rate,channels
54+
returnf.tell(),chunk_size,sample_rate,channels,bits_per_sample
5255
pos+=8+chunk_size
5356
ifchunk_size%2:
5457
pos+=1
@@ -72,21 +75,14 @@ def stop_playing(cls):
7275
cls._keep_running=False
7376

7477
# ------------------------------------------------------------------
75-
#Helper: up-samplea raw PCM buffer (zero-order-hold)
78+
#1. Up-sample16-bit buffer (zero-order-hold)
7679
# ------------------------------------------------------------------
7780
@staticmethod
7881
def_upsample_buffer(raw:bytearray,factor:int)->bytearray:
79-
"""
80-
Duplicate each 16-bit sample `factor` times.
81-
Input: interleaved L,R,L,R... (or mono)
82-
Output: same layout, each sample repeated `factor` times.
83-
"""
8482
iffactor==1:
8583
returnraw
86-
8784
upsampled=bytearray(len(raw)*factor)
8885
out_idx=0
89-
# each sample = 2 bytes
9086
foriinrange(0,len(raw),2):
9187
lo=raw[i]
9288
hi=raw[i+1]
@@ -96,6 +92,64 @@ def _upsample_buffer(raw: bytearray, factor: int) -> bytearray:
9692
out_idx+=2
9793
returnupsampled
9894

95+
# ------------------------------------------------------------------
96+
# 2. Convert 8-bit to 16-bit (non-viper, Viper-safe)
97+
# ------------------------------------------------------------------
98+
@staticmethod
99+
def_convert_8_to_16(buf:bytearray)->bytearray:
100+
out=bytearray(len(buf)*2)
101+
j=0
102+
foriinrange(len(buf)):
103+
u8=buf[i]
104+
s16= (u8-128)<<8
105+
out[j]=s16&0xFF
106+
out[j+1]= (s16>>8)&0xFF
107+
j+=2
108+
returnout
109+
110+
# ------------------------------------------------------------------
111+
# 3. Convert 24-bit to 16-bit (non-viper)
112+
# ------------------------------------------------------------------
113+
@staticmethod
114+
def_convert_24_to_16(buf:bytearray)->bytearray:
115+
samples=len(buf)//3
116+
out=bytearray(samples*2)
117+
j=0
118+
foriinrange(samples):
119+
b0=buf[j]
120+
b1=buf[j+1]
121+
b2=buf[j+2]
122+
s24= (b2<<16)| (b1<<8)|b0
123+
ifb2&0x80:
124+
s24-=0x1000000
125+
s16=s24>>8
126+
out[i*2]=s16&0xFF
127+
out[i*2+1]= (s16>>8)&0xFF
128+
j+=3
129+
returnout
130+
131+
# ------------------------------------------------------------------
132+
# 4. Convert 32-bit to 16-bit (non-viper)
133+
# ------------------------------------------------------------------
134+
@staticmethod
135+
def_convert_32_to_16(buf:bytearray)->bytearray:
136+
samples=len(buf)//4
137+
out=bytearray(samples*2)
138+
j=0
139+
foriinrange(samples):
140+
b0=buf[j]
141+
b1=buf[j+1]
142+
b2=buf[j+2]
143+
b3=buf[j+3]
144+
s32= (b3<<24)| (b2<<16)| (b1<<8)|b0
145+
ifb3&0x80:
146+
s32-=0x100000000
147+
s16=s32>>16
148+
out[i*2]=s16&0xFF
149+
out[i*2+1]= (s16>>8)&0xFF
150+
j+=4
151+
returnout
152+
99153
# ------------------------------------------------------------------
100154
# Main playback routine
101155
# ------------------------------------------------------------------
@@ -109,25 +163,25 @@ def play_wav(cls, filename):
109163
print(f"File size:{file_size} bytes")
110164

111165
# ----- parse header ------------------------------------------------
112-
data_start,data_size,original_rate,channels=cls.find_data_chunk(f)
166+
data_start,data_size,original_rate,channels,bits_per_sample= \
167+
cls.find_data_chunk(f)
113168

114-
# ----- decide playback rate (force >=22050 Hz) --------------------
169+
# ----- decide playback rate (force >=22050 Hz) --------------------
115170
target_rate=22050
116171
iforiginal_rate>=target_rate:
117172
playback_rate=original_rate
118173
upsample_factor=1
119174
else:
120-
# find smallest integer factor so original * factor >= target
121175
upsample_factor= (target_rate+original_rate-1)//original_rate
122176
playback_rate=original_rate*upsample_factor
123177

124-
print(f"Original:{original_rate} Hz → Playback:{playback_rate} Hz "
125-
f"(factor{upsample_factor}),{channels}-ch")
178+
print(f"Original:{original_rate} Hz,{bits_per_sample}-bit,{channels}-ch "
179+
f"to Playback:{playback_rate} Hz(factor{upsample_factor})")
126180

127181
ifdata_size>file_size-data_start:
128182
data_size=file_size-data_start
129183

130-
# ----- I2S init----------------------------------------------------
184+
# ----- I2S init(always 16-bit)----------------------------------
131185
try:
132186
i2s_format=machine.I2S.MONOifchannels==1elsemachine.I2S.STEREO
133187
cls._i2s=machine.I2S(
@@ -144,10 +198,10 @@ def play_wav(cls, filename):
144198
exceptExceptionase:
145199
print(f"Warning: simulating playback (I2S init failed):{e}")
146200

147-
print(f"Playing{data_size} original bytes (vol{cls._volume}%)")
201+
print(f"Playing{data_size} original bytes (vol{cls._volume}%)...")
148202
f.seek(data_start)
149203

150-
# ----- Viper volume scaler (works on any buffer)-------------------
204+
# ----- Viper volume scaler (16-bit only) -------------------------
151205
@micropython.viper
152206
defscale_audio(buf:ptr8,num_bytes:int,scale_fixed:int):
153207
foriinrange(0,num_bytes,2):
@@ -165,15 +219,15 @@ def scale_audio(buf: ptr8, num_bytes: int, scale_fixed: int):
165219
buf[i+1]= (sample>>8)&255
166220

167221
chunk_size=4096
168-
bytes_per_original_sample=2*channels# 2 bytes per channel
222+
bytes_per_original_sample=(bits_per_sample//8)*channels
169223
total_original=0
170224

171225
whiletotal_original<data_size:
172226
ifnotcls._keep_running:
173227
print("Playback stopped by user.")
174228
break
175229

176-
# read a chunk of*original* data
230+
#----read awhole-samplechunk of original data -------------
177231
to_read=min(chunk_size,data_size-total_original)
178232
to_read-= (to_read%bytes_per_original_sample)
179233
ifto_read<=0:
@@ -183,25 +237,33 @@ def scale_audio(buf: ptr8, num_bytes: int, scale_fixed: int):
183237
ifnotraw:
184238
break
185239

186-
# ----- up-sample if needed ---------------------------------
240+
# ---- 1. Convert bit-depth to 16-bit (non-viper) -------------
241+
ifbits_per_sample==8:
242+
raw=cls._convert_8_to_16(raw)
243+
elifbits_per_sample==24:
244+
raw=cls._convert_24_to_16(raw)
245+
elifbits_per_sample==32:
246+
raw=cls._convert_32_to_16(raw)
247+
# 16-bit to unchanged
248+
249+
# ---- 2. Up-sample if needed ---------------------------------
187250
ifupsample_factor>1:
188251
raw=cls._upsample_buffer(raw,upsample_factor)
189252

190-
# ----- volumescaling---------------------------------------
253+
# ---- 3. Volumescaling --------------------------------------
191254
scale=cls._volume/100.0
192255
ifscale<1.0:
193256
scale_fixed=int(scale*32768)
194257
scale_audio(raw,len(raw),scale_fixed)
195258

196-
# ----- output ------------------------------------------------
259+
# ---- 4. Output---------------------------------------------
197260
ifcls._i2s:
198261
cls._i2s.write(raw)
199262
else:
200-
# simulate timing with the *playback* rate
201263
num_samples=len(raw)// (2*channels)
202264
time.sleep(num_samples/playback_rate)
203265

204-
total_original+=to_read# count original bytes only
266+
total_original+=to_read
205267

206268
print("Playback finished.")
207269
exceptExceptionase:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp