Skip to content

Commit 4d17288

Browse files
authored
Convert Sound to Google docstrings + add cross-refs and missing annotations (#2293)
* Convert arcade.sound to Google docstrings * Fix issues from Xero's feedback in the Sound guide * Focus the basics section more tightly on basics and less on theory * Use coin sounds instead of hurt sounds to promote G-rated content * Add centralized argument table for sound loading as prototype for playback args * Finish Google style, cross-refs, and annotations * Convert to Google style docstrings * Add heavy cross-references * Add missing annotations
1 parent f06a1b0 commit 4d17288

File tree

2 files changed

+302
-141
lines changed

2 files changed

+302
-141
lines changed

arcade/sound.py

+169-51
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
"""
2-
Sound Library.
3-
"""
1+
"""Sound Library."""
42

53
from __future__ import annotations
64

@@ -29,7 +27,42 @@
2927

3028

3129
class Sound:
32-
"""This class represents a sound you can play."""
30+
"""Holds :ref:`playable <sound-basics-playing>` loaded audio data.
31+
32+
.. important:: :ref:`Streaming <sound-loading-modes>` disables features!
33+
34+
When ``streaming=True``, :py:meth:`.play` and :py:func:`play_sound`:
35+
36+
* raise a :py:class:`RuntimeError` if there is already another
37+
active playback
38+
* do not support looping
39+
40+
To learn about the restrictions on :ref:`streaming <sound-loading-modes>`,
41+
please see:
42+
43+
* :ref:`sound-loading-modes-streaming`
44+
* The py:class:`pyglet.media.codes.base.StreamingSource` class used
45+
internally
46+
47+
To learn about cross-platform loading and file format concerns,
48+
please see:
49+
50+
* Arcade's sound documentation:
51+
52+
* :ref:`sound-loading-modes`
53+
* :ref:`sound-compat-easy`
54+
* :ref:`sound-compat-loading`
55+
56+
* The pyglet guide to :external+pyglet:ref:`guide-media`
57+
58+
Args:
59+
file_name:
60+
The path of a file to load, optionally prefixed with a
61+
:ref:`resource handle <resource_handles>`.
62+
streaming:
63+
If ``True``, attempt to load data from ``file_path`` via
64+
via :ref:`streaming <sound-loading-modes>`.
65+
"""
3366

3467
def __init__(self, file_name: str | Path, streaming: bool = False):
3568
self.file_name: str = ""
@@ -57,13 +90,26 @@ def play(
5790
loop: bool = False,
5891
speed: float = 1.0,
5992
) -> media.Player:
60-
"""
61-
Play the sound.
93+
"""Try to play this :py:class:`Sound` and return a :py:class:`~pyglet.media.player.Player`.
6294
63-
:param volume: Volume, from 0=quiet to 1=loud
64-
:param pan: Pan, from -1=left to 0=centered to 1=right
65-
:param loop: Loop, false to play once, true to loop continuously
66-
:param speed: Change the speed of the sound which also changes pitch, default 1.0
95+
.. important:: Any :py:class:`Sound` with ``streaming=True`` loses features!
96+
97+
``loop`` will not work and simultaneous playbacks raise
98+
a :py:class:`RuntimeError`.
99+
100+
See the following to learn more about the keywords and restrictions:
101+
102+
* :py:class:`Sound`
103+
* :ref:`sound-advanced-playback-change-aspects-ongoing`
104+
* :ref:`sound-advanced-playback-change-aspects-new`
105+
106+
Args:
107+
volume: Volume (``0.0`` is silent, ``1.0`` is loudest).
108+
pan: Left / right channel balance (``-1`` is left, ``0.0`` is
109+
center, and ``1.0`` is right).
110+
loop: ``True`` attempts to restart playback after finishing.
111+
speed: Change the speed (and pitch) of the sound. Default speed is
112+
``1.0``.
67113
"""
68114
if isinstance(self.source, media.StreamingSource) and self.source.is_player_source:
69115
raise RuntimeError(
@@ -105,72 +151,93 @@ def _on_player_eos():
105151
return player
106152

107153
def stop(self, player: media.Player) -> None:
108-
"""
109-
Stop a currently playing sound.
154+
"""Permanently stop and :py:meth:`~pyglet.media.player.Player.delete` ``player``.
155+
156+
All references in the :py:class:`pyglet.media.Source` player table
157+
will be deleted.
158+
159+
Args:
160+
player: A pyglet :py:class:`~pyglet.media.player.Player`
161+
returned from :func:`play_sound` or :py:meth:`Sound.play`.
110162
"""
111163
player.pause()
112164
player.delete()
113165
if player in media.Source._players:
114166
media.Source._players.remove(player)
115167

116168
def get_length(self) -> float:
117-
"""Get length of audio in seconds"""
169+
"""Get length of the loaded audio in seconds"""
118170
# We validate that duration is known when loading the source
119171
return self.source.duration # type: ignore
120172

121173
def is_complete(self, player: media.Player) -> bool:
122-
"""Return true if the sound is done playing."""
174+
"""``True`` if the sound is done playing."""
123175
# We validate that duration is known when loading the source
124176
return player.time >= self.source.duration # type: ignore
125177

126178
def is_playing(self, player: media.Player) -> bool:
127-
"""
128-
Return if the sound is currently playing or not
179+
"""``True`` if ``player`` is currently playing, otherwise ``False``.
129180
130-
:param player: Player returned from :func:`play_sound`.
131-
:returns: A boolean, ``True`` if the sound is playing.
181+
Args:
182+
player: A pyglet :py:class:`~pyglet.media.player.Player`
183+
returned from :py:meth:`Sound.play <.Sound.play>` or
184+
:func:`play_sound`.
132185
186+
Returns:
187+
``True`` if the passed pyglet player is playing.
133188
"""
134189
return player.playing
135190

136191
def get_volume(self, player: media.Player) -> float:
137-
"""
138-
Get the current volume.
139-
140-
:param player: Player returned from :func:`play_sound`.
141-
:returns: A float, 0 for volume off, 1 for full volume.
192+
"""Get the current volume.
193+
194+
Args:
195+
player: A pyglet :py:class:`~pyglet.media.player.Player`
196+
returned from :py:meth:`Sound.play <.Sound.play>` or
197+
:func:`play_sound`.
198+
Returns:
199+
A volume between ``0.0`` (silent) and ``1.0`` (full volume).
142200
"""
143201
return player.volume # type: ignore # pending https://github.com/pyglet/pyglet/issues/847
144202

145-
def set_volume(self, volume, player: media.Player) -> None:
146-
"""
147-
Set the volume of a sound as it is playing.
203+
def set_volume(self, volume: float, player: media.Player) -> None:
204+
"""Set the volume of a sound as it is playing.
148205
149-
:param volume: Floating point volume. 0 is silent, 1 is full.
150-
:param player: Player returned from :func:`play_sound`.
206+
Args:
207+
volume: Floating point volume. 0 is silent, 1 is full.
208+
player: A pyglet :py:class:`~pyglet.media.player.Player`
209+
returned from :func:`play_sound` or :py:meth:`Sound.play`.
151210
"""
152211
player.volume = volume
153212

154213
def get_stream_position(self, player: media.Player) -> float:
155-
"""
156-
Return where we are in the stream. This will reset back to
214+
"""Return where we are in the stream. This will reset back to
157215
zero when it is done playing.
158216
159-
:param player: Player returned from :func:`play_sound`.
160-
217+
Args:
218+
player: Player returned from :func:`play_sound`.
161219
"""
162220
return player.time
163221

164222

165223
def load_sound(path: str | Path, streaming: bool = False) -> Sound:
166-
"""
167-
Load a sound.
224+
"""Load a file as a :py:class:`Sound` data object.
225+
226+
.. important:: Using ``streaming=True`` disables certain features!
168227
169-
:param path: Name of the sound file to load.
170-
:param streaming: Boolean for determining if we stream the sound
171-
or load it all into memory. Set to ``True`` for long sounds to save
172-
memory, ``False`` for short sounds to speed playback.
173-
:returns: Sound object which can be used by the :func:`play_sound` function.
228+
These include looping and multiple playbacks. Please
229+
see :py:class:`Sound` to learn more.
230+
231+
Args:
232+
path: a path which may be prefixed with a
233+
:ref:`resource_handle <resource_handles>`.
234+
streaming: Boolean for determining if we stream the sound or
235+
load it all into memory. Set to ``True`` for long sounds to
236+
save memory, ``False`` for short sounds to speed playback.
237+
238+
Returns:
239+
A :ref:playable <sound-basics-playing>` instance of a
240+
:py:class:`Sound` object.
174241
"""
175242
# Initialize the audio driver if it hasn't been already.
176243
# This call is to avoid audio driver initialization
@@ -188,20 +255,69 @@ def load_sound(path: str | Path, streaming: bool = False) -> Sound:
188255

189256

190257
def play_sound(
191-
sound: Sound,
258+
sound: Sound | None,
192259
volume: float = 1.0,
193260
pan: float = 0.0,
194261
loop: bool = False,
195262
speed: float = 1.0,
196263
) -> media.Player | None:
197-
"""
198-
Play a sound.
264+
"""Try to play the ``sound`` and return a :py:class:`~pyglet.media.player.Player`.
265+
266+
.. note:: The ``sound`` **must** be a :py:class:`Sound` object!
267+
268+
See the following to load audio from file paths:
269+
270+
* :ref:`sound-basics-loading`
271+
* :ref:`sound-loading-modes`
272+
* :py:func:`load_sound`
273+
* :py:class:`Sound`
274+
275+
The output and return value depend on whether playback succeeded:
276+
277+
.. list-table::
278+
:header-rows: 1
279+
280+
* - Success?
281+
- Console output
282+
- Return value
283+
284+
* - No / ``sound`` is ``None``
285+
- Log a warning
286+
- ``None``
287+
288+
* - Yes
289+
- N/A
290+
- A pyglet :py:class:`~pyglet.media.player.Player`
291+
292+
See the following to learn more:
293+
294+
* :ref:`sound-basics-sound_vs_player`
295+
* :ref:`sound-advanced-playback`
296+
297+
.. important:: Any :py:class:`Sound` with ``streaming=True`` loses features!
298+
299+
``loop`` will not work and simultaneous playbacks raise
300+
a :py:class:`RuntimeError`.
301+
302+
To learn more about the ``streaming`` keyword and restrictions, please see:
303+
304+
* :py:class:`Sound`
305+
* :ref:`sound-advanced-playback-change-aspects-ongoing`
306+
* :ref:`sound-advanced-playback-change-aspects-new`
307+
308+
Args:
309+
sound: A :py:class:`Sound` instance or ``None``.
310+
volume: From ``0.0`` (silent) to ``1.0`` (max volume).
311+
pan: The left / right ear balance (``-1`` is left, ``0`` is center,
312+
and ``1`` is right)
313+
loop: ``True`` makes playback restart each time it reaches the end.
314+
speed: How fast to play. Slower than ``1.0`` deepens sound while
315+
values higher than ``1.0`` raise the pitch.
316+
317+
Returns:
318+
A :py:class:`pyglet.media.Player` instance for the playback or
319+
``None`` if playback failed.
199320
200-
:param sound: Sound loaded by :func:`load_sound`. Do NOT use a string here for the filename.
201-
:param volume: Volume, from 0=quiet to 1=loud
202-
:param pan: Pan, from -1=left to 0=centered to 1=right
203-
:param loop: Should we loop the sound over and over?
204-
:param speed: Change the speed of the sound which also changes pitch, default 1.0
205321
"""
206322
if sound is None:
207323
logger.warning("Unable to play sound, no data passed in.")
@@ -221,11 +337,13 @@ def play_sound(
221337
return None
222338

223339

224-
def stop_sound(player: media.Player):
225-
"""
226-
Stop a sound that is currently playing.
340+
def stop_sound(player: media.Player) -> None:
341+
"""Stop a pyglet player for a which is currently playing.
227342
228-
:param player: Player returned from :func:`play_sound`.
343+
Args:
344+
player: A pyglet :py:class:`~pyglet.media.player.Player`
345+
returned from :py:meth:`Sound.play <.Sound.play>` or
346+
:func:`play_sound`.
229347
"""
230348

231349
if not isinstance(player, media.Player):

0 commit comments

Comments
 (0)