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

Commitd3be0ce

Browse files
AudioPlayer: upscale if low frequency
1 parent1984857 commitd3be0ce

File tree

1 file changed

+70
-22
lines changed
  • internal_filesystem/apps/com.micropythonos.musicplayer/assets

1 file changed

+70
-22
lines changed

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

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66

77
# ----------------------------------------------------------------------
88
# AudioPlayer – robust, volume-controllable WAV player (MONO + STEREO)
9+
# Auto-up-samples any rate < 22050 Hz to 22050 Hz for MAX98357
910
# ----------------------------------------------------------------------
1011
classAudioPlayer:
11-
# class-level defaults
1212
_i2s=None
13-
_volume=50# 0-100
13+
_volume=50# 0-100
1414
_keep_running=True
1515

16+
# ------------------------------------------------------------------
17+
# WAV header parser
18+
# ------------------------------------------------------------------
1619
@staticmethod
1720
deffind_data_chunk(f):
18-
"""Skip chunks until 'data' is found → (data_start, data_size, sample_rate, channels)"""
21+
"""Return (data_start, data_size, sample_rate, channels)"""
1922
f.seek(0)
2023
iff.read(4)!=b'RIFF':
2124
raiseValueError("Not a RIFF file")
@@ -68,6 +71,34 @@ def stop_playing(cls):
6871
print("stop_playing()")
6972
cls._keep_running=False
7073

74+
# ------------------------------------------------------------------
75+
# Helper: up-sample a raw PCM buffer (zero-order-hold)
76+
# ------------------------------------------------------------------
77+
@staticmethod
78+
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+
"""
84+
iffactor==1:
85+
returnraw
86+
87+
upsampled=bytearray(len(raw)*factor)
88+
out_idx=0
89+
# each sample = 2 bytes
90+
foriinrange(0,len(raw),2):
91+
lo=raw[i]
92+
hi=raw[i+1]
93+
for_inrange(factor):
94+
upsampled[out_idx]=lo
95+
upsampled[out_idx+1]=hi
96+
out_idx+=2
97+
returnupsampled
98+
99+
# ------------------------------------------------------------------
100+
# Main playback routine
101+
# ------------------------------------------------------------------
71102
@classmethod
72103
defplay_wav(cls,filename):
73104
cls._keep_running=True
@@ -77,15 +108,28 @@ def play_wav(cls, filename):
77108
file_size=st[6]
78109
print(f"File size:{file_size} bytes")
79110

80-
data_start,data_size,sample_rate,channels=cls.find_data_chunk(f)
81-
print(f"data chunk:{data_size} bytes @{sample_rate} Hz,{channels}-channel")
111+
# ----- parse header ------------------------------------------------
112+
data_start,data_size,original_rate,channels=cls.find_data_chunk(f)
113+
114+
# ----- decide playback rate (force >= 22050 Hz) --------------------
115+
target_rate=22050
116+
iforiginal_rate>=target_rate:
117+
playback_rate=original_rate
118+
upsample_factor=1
119+
else:
120+
# find smallest integer factor so original * factor >= target
121+
upsample_factor= (target_rate+original_rate-1)//original_rate
122+
playback_rate=original_rate*upsample_factor
123+
124+
print(f"Original:{original_rate} Hz → Playback:{playback_rate} Hz "
125+
f"(factor{upsample_factor}),{channels}-ch")
82126

83127
ifdata_size>file_size-data_start:
84128
data_size=file_size-data_start
85129

86-
# ---- I2S init ------------------------------------------------
87-
i2s_format=machine.I2S.MONOifchannels==1elsemachine.I2S.STEREO
130+
# ----- I2S init ----------------------------------------------------
88131
try:
132+
i2s_format=machine.I2S.MONOifchannels==1elsemachine.I2S.STEREO
89133
cls._i2s=machine.I2S(
90134
0,
91135
sck=machine.Pin(2,machine.Pin.OUT),
@@ -94,18 +138,18 @@ def play_wav(cls, filename):
94138
mode=machine.I2S.TX,
95139
bits=16,
96140
format=i2s_format,
97-
rate=sample_rate,
141+
rate=playback_rate,
98142
ibuf=32000
99143
)
100144
exceptExceptionase:
101145
print(f"Warning: simulating playback (I2S init failed):{e}")
102146

103-
print(f"Playing{data_size} bytes (vol{cls._volume}%) …")
147+
print(f"Playing{data_size}originalbytes (vol{cls._volume}%) …")
104148
f.seek(data_start)
105149

150+
# ----- Viper volume scaler (works on any buffer) -------------------
106151
@micropython.viper
107152
defscale_audio(buf:ptr8,num_bytes:int,scale_fixed:int):
108-
# Process 16-bit samples (2 bytes each)
109153
foriinrange(0,num_bytes,2):
110154
lo=int(buf[i])
111155
hi=int(buf[i+1])
@@ -121,39 +165,43 @@ def scale_audio(buf: ptr8, num_bytes: int, scale_fixed: int):
121165
buf[i+1]= (sample>>8)&255
122166

123167
chunk_size=4096
124-
bytes_per_sample=2*channels# 2 bytes per channel
125-
total=0
168+
bytes_per_original_sample=2*channels# 2 bytes per channel
169+
total_original=0
126170

127-
whiletotal<data_size:
171+
whiletotal_original<data_size:
128172
ifnotcls._keep_running:
129173
print("Playback stopped by user.")
130174
break
131175

132-
to_read=min(chunk_size,data_size-total)
133-
# Ensure we read full samples
134-
to_read-= (to_read%bytes_per_sample)
176+
# read a chunk of *original* data
177+
to_read=min(chunk_size,data_size-total_original)
178+
to_read-= (to_read%bytes_per_original_sample)
135179
ifto_read<=0:
136180
break
137181

138182
raw=bytearray(f.read(to_read))
139183
ifnotraw:
140184
break
141185

142-
# Apply volume scaling (in-place, per sample)
186+
# ----- up-sample if needed ---------------------------------
187+
ifupsample_factor>1:
188+
raw=cls._upsample_buffer(raw,upsample_factor)
189+
190+
# ----- volume scaling ---------------------------------------
143191
scale=cls._volume/100.0
144192
ifscale<1.0:
145193
scale_fixed=int(scale*32768)
146194
scale_audio(raw,len(raw),scale_fixed)
147195

148-
#Write to I2S (stereo interleaves L,R,L,R...)
196+
#----- output ------------------------------------------------
149197
ifcls._i2s:
150198
cls._i2s.write(raw)
151199
else:
152-
#Simulate timing
153-
num_samples=len(raw)//bytes_per_sample
154-
time.sleep(num_samples/sample_rate)
200+
#simulate timing with the *playback* rate
201+
num_samples=len(raw)//(2*channels)
202+
time.sleep(num_samples/playback_rate)
155203

156-
total+=len(raw)
204+
total_original+=to_read# count original bytes only
157205

158206
print("Playback finished.")
159207
exceptExceptionase:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp