1+ import ui
2+ import sound
3+ import console
4+
5+ # 内置音效分类和示例音效
6+ AUDIO_CATEGORIES = {
7+ "游戏音效 (game)" : [
8+ "game:Beep" , "game:Hit_1" , "game:Hit_2" , "game:Hit_3" , "game:Powerup" ,
9+ "game:Error" , "game:Coin_1" , "game:Coin_2" , "game:Coin_3" , "game:Jump_1" ,
10+ "game:Jump_2" , "game:Explosion_1" , "game:Explosion_2" , "game:Explosion_3" ,
11+ "game:Laser_1" , "game:Laser_2" , "game:Laser_3" , "game:Shoot_1" , "game:Shoot_2" ,
12+ "game:Shoot_3" , "game:Select_1" , "game:Select_2" , "game:Select_3"
13+ ],
14+ "街机音效 (arcade)" : [
15+ "arcade:Coin_1" , "arcade:Coin_2" , "arcade:Coin_3" , "arcade:Jump_1" ,
16+ "arcade:Jump_2" , "arcade:Explosion_1" , "arcade:Explosion_2" , "arcade:Explosion_3" ,
17+ "arcade:Laser_1" , "arcade:Laser_2" , "arcade:Laser_3" , "arcade:Shoot_1" ,
18+ "arcade:Shoot_2" , "arcade:Shoot_3" , "arcade:Select_1" , "arcade:Select_2" ,
19+ "arcade:Select_3" , "arcade:Powerup_1" , "arcade:Powerup_2" , "arcade:Powerup_3"
20+ ],
21+ "UI 音效 (ui)" : [
22+ "ui:click1" , "ui:click2" , "ui:click3" , "ui:click4" , "ui:click5" ,
23+ "ui:pop1" , "ui:pop2" , "ui:pop3" , "ui:pop4" , "ui:pop5" ,
24+ "ui:alert1" , "ui:alert2" , "ui:alert3" , "ui:alert4" , "ui:alert5" ,
25+ "ui:success1" , "ui:success2" , "ui:success3" , "ui:success4" , "ui:success5"
26+ ],
27+ "数字音效 (digital)" : [
28+ "digital:beep1" , "digital:beep2" , "digital:beep3" , "digital:beep4" , "digital:beep5" ,
29+ "digital:error1" , "digital:error2" , "digital:error3" , "digital:error4" , "digital:error5" ,
30+ "digital:success1" , "digital:success2" , "digital:success3" , "digital:success4" , "digital:success5"
31+ ],
32+ "赌场音效 (casino)" : [
33+ "casino:chip1" , "casino:chip2" , "casino:chip3" , "casino:chip4" , "casino:chip5" ,
34+ "casino:coin1" , "casino:coin2" , "casino:coin3" , "casino:coin4" , "casino:coin5" ,
35+ "casino:slot1" , "casino:slot2" , "casino:slot3" , "casino:slot4" , "casino:slot5"
36+ ],
37+ "RPG 音效 (rpg)" : [
38+ "rpg:DoorOpen_1" , "rpg:DoorClose_1" , "rpg:Footstep_1" , "rpg:Footstep_2" , "rpg:Footstep_3" ,
39+ "rpg:SwordSwing_1" , "rpg:SwordSwing_2" , "rpg:SwordSwing_3" , "rpg:KnifeSlice_1" , "rpg:KnifeSlice_2" ,
40+ "rpg:MagicSpell_1" , "rpg:MagicSpell_2" , "rpg:MagicSpell_3" , "rpg:MagicSpell_4" , "rpg:MagicSpell_5"
41+ ],
42+ "音乐音效 (music)" : [
43+ "music:Victory_NES_1" , "music:Victory_NES_2" , "music:Victory_NES_3" , "music:GameOver_NES_1" ,
44+ "music:GameOver_NES_2" , "music:GameOver_NES_3" , "music:LevelUp_NES_1" , "music:LevelUp_NES_2" ,
45+ "music:LevelUp_NES_3" , "music:Intro_NES_1" , "music:Intro_NES_2" , "music:Intro_NES_3"
46+ ]
47+ }
48+
49+ class AudioTester :
50+ def __init__ (self ):
51+ self .current_sound = None
52+ self .current_volume = 0.5
53+ self .current_pitch = 1.0
54+ self .current_pan = 0.0
55+
56+ # 创建主视图
57+ self .view = ui .View (frame = (0 , 0 , 400 , 700 ))
58+ self .view .name = '音频测试器'
59+ self .view .background_color = '#f5f5f5'
60+
61+ # 标题
62+ title_label = ui .Label (frame = (20 , 20 , 360 , 40 ))
63+ title_label .text = '🎵 内置音频测试器'
64+ title_label .font = ('System-Bold' , 24 )
65+ title_label .alignment = ui .ALIGN_CENTER
66+ title_label .text_color = '#333'
67+ title_label .flex = 'W'
68+ self .view .add_subview (title_label )
69+
70+ # 统计标签
71+ total_sounds = sum (len (sounds ) for sounds in AUDIO_CATEGORIES .values ())
72+ stats_label = ui .Label (frame = (20 , 70 , 360 , 20 ))
73+ stats_label .text = f'共 { total_sounds } 个内置音效,7 大分类'
74+ stats_label .font = ('System' , 14 )
75+ stats_label .alignment = ui .ALIGN_CENTER
76+ stats_label .text_color = '#666'
77+ stats_label .flex = 'W'
78+ self .view .add_subview (stats_label )
79+
80+ # 控制面板
81+ control_view = ui .View (frame = (20 , 100 , 360 , 120 ))
82+ control_view .background_color = '#ffffff'
83+ control_view .corner_radius = 10
84+ control_view .border_width = 1
85+ control_view .border_color = '#e0e0e0'
86+ control_view .flex = 'W'
87+ self .view .add_subview (control_view )
88+
89+ # 音量控制
90+ volume_label = ui .Label (frame = (20 , 15 , 80 , 30 ))
91+ volume_label .text = '音量:'
92+ volume_label .font = ('System' , 14 )
93+ control_view .add_subview (volume_label )
94+
95+ self .volume_slider = ui .Slider (frame = (100 , 15 , 200 , 30 ))
96+ self .volume_slider .value = self .current_volume
97+ self .volume_slider .continuous = True
98+ self .volume_slider .action = self .volume_changed
99+ control_view .add_subview (self .volume_slider )
100+
101+ self .volume_value_label = ui .Label (frame = (310 , 15 , 40 , 30 ))
102+ self .volume_value_label .text = f'{ int (self .current_volume * 100 )} %'
103+ self .volume_value_label .font = ('System' , 14 )
104+ self .volume_value_label .alignment = ui .ALIGN_RIGHT
105+ control_view .add_subview (self .volume_value_label )
106+
107+ # 音高控制
108+ pitch_label = ui .Label (frame = (20 , 55 , 80 , 30 ))
109+ pitch_label .text = '音高:'
110+ pitch_label .font = ('System' , 14 )
111+ control_view .add_subview (pitch_label )
112+
113+ self .pitch_slider = ui .Slider (frame = (100 , 55 , 200 , 30 ))
114+ self .pitch_slider .value = 0.5 # 映射到 0.5-2.0
115+ self .pitch_slider .continuous = True
116+ self .pitch_slider .action = self .pitch_changed
117+ control_view .add_subview (self .pitch_slider )
118+
119+ self .pitch_value_label = ui .Label (frame = (310 , 55 , 40 , 30 ))
120+ self .pitch_value_label .text = f'{ self .current_pitch :.1f} x'
121+ self .pitch_value_label .font = ('System' , 14 )
122+ self .pitch_value_label .alignment = ui .ALIGN_RIGHT
123+ control_view .add_subview (self .pitch_value_label )
124+
125+ # 声像控制
126+ pan_label = ui .Label (frame = (20 , 95 , 80 , 30 ))
127+ pan_label .text = '声像:'
128+ pan_label .font = ('System' , 14 )
129+ control_view .add_subview (pan_label )
130+
131+ self .pan_slider = ui .Slider (frame = (100 , 95 , 200 , 30 ))
132+ self .pan_slider .value = 0.5 # 映射到 -1.0到1.0
133+ self .pan_slider .continuous = True
134+ self .pan_slider .action = self .pan_changed
135+ control_view .add_subview (self .pan_slider )
136+
137+ self .pan_value_label = ui .Label (frame = (310 , 95 , 40 , 30 ))
138+ self .pan_value_label .text = f'{ self .current_pan :.1f} '
139+ self .pan_value_label .font = ('System' , 14 )
140+ self .pan_value_label .alignment = ui .ALIGN_RIGHT
141+ control_view .add_subview (self .pan_value_label )
142+
143+ # 控制按钮
144+ button_y = 230
145+ self .play_button = ui .Button (frame = (20 , button_y , 160 , 44 ))
146+ self .play_button .title = '▶️ 播放选中音效'
147+ self .play_button .background_color = '#4CAF50'
148+ self .play_button .title_color = 'white'
149+ self .play_button .corner_radius = 8
150+ self .play_button .action = self .play_sound
151+ self .play_button .flex = 'LRTB'
152+ self .view .add_subview (self .play_button )
153+
154+ self .stop_button = ui .Button (frame = (200 , button_y , 160 , 44 ))
155+ self .stop_button .title = '⏹️ 停止所有音效'
156+ self .stop_button .background_color = '#F44336'
157+ self .stop_button .title_color = 'white'
158+ self .stop_button .corner_radius = 8
159+ self .stop_button .action = self .stop_all_sounds
160+ self .stop_button .flex = 'LRTB'
161+ self .view .add_subview (self .stop_button )
162+
163+ # 当前播放标签
164+ self .current_label = ui .Label (frame = (20 , 290 , 360 , 30 ))
165+ self .current_label .text = '当前播放: 无'
166+ self .current_label .font = ('System' , 14 )
167+ self .current_label .text_color = '#666'
168+ self .current_label .flex = 'W'
169+ self .view .add_subview (self .current_label )
170+
171+ # 创建分段控件用于切换分类
172+ self .segmented_control = ui .SegmentedControl (frame = (20 , 330 , 360 , 32 ))
173+ self .segmented_control .segments = list (AUDIO_CATEGORIES .keys ())
174+ self .segmented_control .selected_index = 0
175+ self .segmented_control .action = self .category_changed
176+ self .segmented_control .flex = 'W'
177+ self .view .add_subview (self .segmented_control )
178+
179+ # 创建表格视图显示音效列表
180+ self .table_view = ui .TableView (frame = (20 , 380 , 360 , 280 ))
181+ self .table_view .row_height = 44
182+ self .table_view .flex = 'WH'
183+ self .table_view .corner_radius = 8
184+ self .table_view .border_width = 1
185+ self .table_view .border_color = '#e0e0e0'
186+ self .table_view .delegate = self
187+ self .view .add_subview (self .table_view )
188+
189+ # 初始化数据
190+ self .current_category = list (AUDIO_CATEGORIES .keys ())[0 ]
191+ self .current_sounds = AUDIO_CATEGORIES [self .current_category ]
192+ self .selected_sound = None
193+
194+ # 设置 TableView 数据源
195+ self .update_table_data ()
196+
197+ def volume_changed (self , sender ):
198+ self .current_volume = sender .value
199+ self .volume_value_label .text = f'{ int (self .current_volume * 100 )} %'
200+
201+ def pitch_changed (self , sender ):
202+ # 将 0-1 映射到 0.5-2.0
203+ self .current_pitch = 0.5 + sender .value * 1.5
204+ self .pitch_value_label .text = f'{ self .current_pitch :.1f} x'
205+
206+ def pan_changed (self , sender ):
207+ # 将 0-1 映射到 -1.0到1.0
208+ self .current_pan = (sender .value - 0.5 ) * 2
209+ self .pan_value_label .text = f'{ self .current_pan :.1f} '
210+
211+ def category_changed (self , sender ):
212+ self .current_category = sender .segments [sender .selected_index ]
213+ self .current_sounds = AUDIO_CATEGORIES [self .current_category ]
214+ self .update_table_data ()
215+ self .selected_sound = None
216+ self .current_label .text = '当前播放: 无'
217+
218+ def update_table_data (self ):
219+ # 将音效列表转换为 TableView 可用的数据格式
220+ table_data = []
221+ for sound_name in self .current_sounds :
222+ table_data .append ({
223+ 'title' : sound_name ,
224+ 'accessory_type' : 'disclosure_indicator'
225+ })
226+ self .table_view .data_source = table_data
227+ self .table_view .reload_data ()
228+
229+ def play_sound (self , sender ):
230+ if self .selected_sound :
231+ try :
232+ # 播放音效
233+ self .current_sound = sound .play_effect (
234+ self .selected_sound ,
235+ volume = self .current_volume ,
236+ pitch = self .current_pitch ,
237+ pan = self .current_pan
238+ )
239+ self .current_label .text = f'当前播放: { self .selected_sound } '
240+ console .hud_alert (f'播放: { self .selected_sound } ' , duration = 1.0 )
241+ except Exception as e :
242+ console .hud_alert (f'播放失败: { str (e )} ' , duration = 2.0 )
243+ else :
244+ console .hud_alert ('请先选择一个音效' , duration = 1.5 )
245+
246+ def stop_all_sounds (self , sender ):
247+ sound .stop_all_effects ()
248+ self .current_label .text = '当前播放: 已停止'
249+ console .hud_alert ('已停止所有音效' , duration = 1.0 )
250+
251+ def tableview_did_select (self , tableview , section , row ):
252+ self .selected_sound = self .current_sounds [row ]
253+ self .current_label .text = f'已选择: { self .selected_sound } '
254+ # 自动播放选中的音效
255+ self .play_sound (None )
256+
257+
258+
259+ def run (self ):
260+ self .view .present ('sheet' )
261+
262+ # 创建并运行音频测试器
263+ if __name__ == '__main__' :
264+ tester = AudioTester ()
265+ tester .run ()
0 commit comments