1"""
2Sound Panning Demo
3
4If Python and Arcade are installed, this example can be run from the
5command line with:
6python -m arcade.examples.sound_demo
7
8Each button plays a sound when clicked.
9
10The top left button plays a streaming music track when pressed. If you
11click it while it's already playing, it will intentionally crash the
12demo to demonstrate how you shouldn't try to play a streaming sound
13that's already playing.
14
15The lower 3 rows of buttons play a non-streaming (static) sound with
16different panning and volume. Going from left to right changes the
17panning, which is how much the sound plays in the left speaker vs the
18right speaker. Lower rows play the sound louder than the higher ones.
19"""
20
21from __future__ import annotations
22
23import typing
24
25import arcade
26
27SCREEN_WIDTH = 800
28SCREEN_HEIGHT = 600
29SCREEN_TITLE = "Sound Panning Demo"
30BUTTON_SIZE = 30
31
32
33SOUND_PANNING = [-1.0, -0.5, 0.0, 0.5, 1.0]
34BUTTON_X_POSITIONS = [
35 BUTTON_SIZE,
36 SCREEN_WIDTH / 4,
37 SCREEN_WIDTH / 2,
38 SCREEN_WIDTH / 4 * 3,
39 SCREEN_WIDTH - BUTTON_SIZE,
40]
41
42
43VOLUME_VARIATION = [0.1, 0.5, 1]
44Y_OFFSETS = [50, 0, -50]
45
46
47class SoundButton(arcade.SpriteSolidColor):
48 """
49 A sprite that stores settings about how to play a sound.
50
51 This class can load a sound as either a static sound or a streaming
52 sound. Streaming should be used for long files that will only have
53 one instance playing, such as music or ambiance tracks.
54
55 If you try to play a sound created with streaming=True while it is
56 already playing, it will raise an exception! Non-streaming (static)
57 sounds are fine with it, and can have play() called on them as many
58 times as you want.
59 """
60
61 def __init__(
62 self,
63 sound_file,
64 pan=0.5,
65 volume=0.5,
66 center_x=0,
67 center_y=0,
68 streaming=False
69 ):
70 super().__init__(BUTTON_SIZE, BUTTON_SIZE, color=arcade.color.WHITE)
71 self.sound = arcade.Sound(sound_file, streaming=streaming)
72 self.pan = pan
73 self.volume = volume
74 self.center_x = center_x
75 self.center_y = center_y
76
77 def play(self):
78 self.sound.play(pan=self.pan, volume=self.volume)
79
80
81class MyGame(arcade.Window):
82 def __init__(self, width, height, title):
83 super().__init__(width, height, title)
84 self.background_color = arcade.color.AMAZON
85 self.button_sprites = None
86
87 def setup(self):
88 self.button_sprites = arcade.SpriteList()
89
90 # create the streaming button at the top left
91 self.button_sprites.append(
92 SoundButton(
93 ":resources:music/funkyrobot.mp3",
94 pan=-1.0,
95 volume=0.1,
96 center_x=BUTTON_SIZE,
97 center_y=SCREEN_HEIGHT / 2 + 150,
98 streaming=True
99 )
100 )
101
102 # Position the grid of buttons
103 # The zip function takes pieces from iterables and returns them
104 # as tuples. For more information, see the python doc:
105 # https://docs.python.org/3/library/functions.html#zip
106 for vol, y_offset in zip(VOLUME_VARIATION, Y_OFFSETS):
107 for pan_setting, x_pos in zip(SOUND_PANNING, BUTTON_X_POSITIONS):
108 self.button_sprites.append(
109 SoundButton(
110 ":resources:sounds/upgrade4.wav",
111 pan_setting,
112 vol,
113 x_pos,
114 SCREEN_HEIGHT / 2 + y_offset,
115 )
116 )
117
118 def on_draw(self):
119 self.clear()
120 self.button_sprites.draw()
121
122 def on_update(self, delta_time):
123 self.button_sprites.update()
124
125 def on_mouse_press(self, x, y, button, key_modifiers):
126 hit_sprites = arcade.get_sprites_at_point((x, y), self.button_sprites)
127 for sprite in hit_sprites:
128 button_sprite = typing.cast(SoundButton, sprite)
129 if button == arcade.MOUSE_BUTTON_LEFT:
130 button_sprite.play()
131
132
133def main():
134 game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
135 game.setup()
136 arcade.run()
137
138
139if __name__ == "__main__":
140 main()