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

Commit1984857

Browse files
AudioPlayer: support stereo
1 parentf43684a commit1984857

File tree

1 file changed

+39
-33
lines changed
  • internal_filesystem/apps/com.micropythonos.musicplayer/assets

1 file changed

+39
-33
lines changed

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

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55

66

77
# ----------------------------------------------------------------------
8-
# AudioPlayer – robust, volume-controllable WAV player
8+
# AudioPlayer – robust, volume-controllable WAV player (MONO + STEREO)
99
# ----------------------------------------------------------------------
1010
classAudioPlayer:
11-
# class-level defaults (shared by every instance)
12-
_i2s=None# the I2S object (created once per playback)
13-
_volume=50# 0-100 (100 = full scale)
11+
# class-level defaults
12+
_i2s=None
13+
_volume=50# 0-100
1414
_keep_running=True
1515

1616
@staticmethod
1717
deffind_data_chunk(f):
18-
"""Skip chunks until 'data' is found → (data_start, data_size, sample_rate)"""
18+
"""Skip chunks until 'data' is found → (data_start, data_size, sample_rate, channels)"""
1919
f.seek(0)
2020
iff.read(4)!=b'RIFF':
2121
raiseValueError("Not a RIFF file")
@@ -25,6 +25,7 @@ def find_data_chunk(f):
2525

2626
pos=12
2727
sample_rate=None
28+
channels=None
2829
whilepos<file_size:
2930
f.seek(pos)
3031
chunk_id=f.read(4)
@@ -38,14 +39,13 @@ def find_data_chunk(f):
3839
ifint.from_bytes(fmt[0:2],'little')!=1:
3940
raiseValueError("Only PCM supported")
4041
channels=int.from_bytes(fmt[2:4],'little')
41-
ifchannels!=1:
42-
raiseValueError("Only mono supported")
42+
ifchannelsnotin (1,2):
43+
raiseValueError("Only monoor stereosupported")
4344
sample_rate=int.from_bytes(fmt[4:8],'little')
4445
ifint.from_bytes(fmt[14:16],'little')!=16:
4546
raiseValueError("Only 16-bit supported")
4647
elifchunk_id==b'data':
47-
returnf.tell(),chunk_size,sample_rate
48-
# next chunk (pad byte if odd length)
48+
returnf.tell(),chunk_size,sample_rate,channels
4949
pos+=8+chunk_size
5050
ifchunk_size%2:
5151
pos+=1
@@ -56,37 +56,35 @@ def find_data_chunk(f):
5656
# ------------------------------------------------------------------
5757
@classmethod
5858
defset_volume(cls,volume:int):
59-
"""Set playback volume 0-100 (100 = full scale)."""
60-
volume=max(0,min(100,volume))# clamp
59+
volume=max(0,min(100,volume))
6160
cls._volume=volume
6261

6362
@classmethod
6463
defget_volume(cls)->int:
65-
"""Return current volume 0-100."""
6664
returncls._volume
6765

68-
#@classmethod
69-
defstop_playing():
66+
@classmethod
67+
defstop_playing(cls):
7068
print("stop_playing()")
71-
AudioPlayer._keep_running=False
69+
cls._keep_running=False
7270

7371
@classmethod
7472
defplay_wav(cls,filename):
75-
AudioPlayer._keep_running=True
76-
"""Play a large mono 16-bit PCM WAV file with on-the-fly volume."""
73+
cls._keep_running=True
7774
try:
7875
withopen(filename,'rb')asf:
7976
st=os.stat(filename)
8077
file_size=st[6]
8178
print(f"File size:{file_size} bytes")
8279

83-
data_start,data_size,sample_rate=cls.find_data_chunk(f)
84-
print(f"data chunk:{data_size} bytes @{sample_rate} Hz")
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")
8582

8683
ifdata_size>file_size-data_start:
8784
data_size=file_size-data_start
8885

8986
# ---- I2S init ------------------------------------------------
87+
i2s_format=machine.I2S.MONOifchannels==1elsemachine.I2S.STEREO
9088
try:
9189
cls._i2s=machine.I2S(
9290
0,
@@ -95,18 +93,19 @@ def play_wav(cls, filename):
9593
sd=machine.Pin(16,machine.Pin.OUT),
9694
mode=machine.I2S.TX,
9795
bits=16,
98-
format=machine.I2S.MONO,
96+
format=i2s_format,
9997
rate=sample_rate,
10098
ibuf=32000
10199
)
102100
exceptExceptionase:
103-
print(f"Warning: simulating playbackdue to error initializingI2Saudio device:{e}")
101+
print(f"Warning: simulating playback(I2Sinit failed):{e}")
104102

105103
print(f"Playing{data_size} bytes (vol{cls._volume}%) …")
106104
f.seek(data_start)
107105

108106
@micropython.viper
109107
defscale_audio(buf:ptr8,num_bytes:int,scale_fixed:int):
108+
# Process 16-bit samples (2 bytes each)
110109
foriinrange(0,num_bytes,2):
111110
lo=int(buf[i])
112111
hi=int(buf[i+1])
@@ -121,32 +120,39 @@ def scale_audio(buf: ptr8, num_bytes: int, scale_fixed: int):
121120
buf[i]=sample&255
122121
buf[i+1]= (sample>>8)&255
123122

124-
chunk_size=4096# 4 KB → safe on ESP32
125-
123+
chunk_size=4096
124+
bytes_per_sample=2*channels# 2 bytes per channel
126125
total=0
126+
127127
whiletotal<data_size:
128-
# Progress:
129-
#if total % 51 == 0:
130-
# print('.', end='')
131-
ifnotAudioPlayer._keep_running:
132-
print("_keep_running = False, stopping...")
128+
ifnotcls._keep_running:
129+
print("Playback stopped by user.")
133130
break
131+
134132
to_read=min(chunk_size,data_size-total)
135-
raw=bytearray(f.read(to_read))# mutable for in-place scaling
133+
# Ensure we read full samples
134+
to_read-= (to_read%bytes_per_sample)
135+
ifto_read<=0:
136+
break
137+
138+
raw=bytearray(f.read(to_read))
136139
ifnotraw:
137140
break
138141

139-
#---- fast viperscaling (in-place) ----
140-
scale=cls._volume/100.0# adjust the volume on the fly instead of at the start of playback
142+
#Apply volumescaling (in-place, per sample)
143+
scale=cls._volume/100.0
141144
ifscale<1.0:
142145
scale_fixed=int(scale*32768)
143146
scale_audio(raw,len(raw),scale_fixed)
144-
# ---------------------------------------
145147

148+
# Write to I2S (stereo interleaves L,R,L,R...)
146149
ifcls._i2s:
147150
cls._i2s.write(raw)
148151
else:
149-
time.sleep((to_read/2)/44100)# 16 bits (2 bytes) per sample at 44100 samples/s
152+
# Simulate timing
153+
num_samples=len(raw)//bytes_per_sample
154+
time.sleep(num_samples/sample_rate)
155+
150156
total+=len(raw)
151157

152158
print("Playback finished.")

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp