pyglet 2.1.12__py3-none-any.whl → 3.0.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. pyglet/__init__.py +67 -61
  2. pyglet/__init__.pyi +15 -8
  3. pyglet/app/__init__.py +22 -13
  4. pyglet/app/async_app.py +212 -0
  5. pyglet/app/base.py +2 -1
  6. pyglet/app/{xlib.py → linux.py} +3 -3
  7. pyglet/config/__init__.py +101 -0
  8. pyglet/config/gl/__init__.py +30 -0
  9. pyglet/config/gl/egl.py +120 -0
  10. pyglet/config/gl/macos.py +262 -0
  11. pyglet/config/gl/windows.py +267 -0
  12. pyglet/config/gl/x11.py +142 -0
  13. pyglet/customtypes.py +43 -2
  14. pyglet/display/__init__.py +8 -6
  15. pyglet/display/base.py +3 -63
  16. pyglet/display/cocoa.py +12 -17
  17. pyglet/display/emscripten.py +39 -0
  18. pyglet/display/headless.py +23 -30
  19. pyglet/display/wayland.py +157 -0
  20. pyglet/display/win32.py +4 -17
  21. pyglet/display/xlib.py +19 -27
  22. pyglet/display/xlib_vidmoderestore.py +2 -2
  23. pyglet/enums.py +183 -0
  24. pyglet/event.py +0 -1
  25. pyglet/experimental/geoshader_sprite.py +15 -13
  26. pyglet/experimental/hidraw.py +6 -15
  27. pyglet/experimental/multitexture_sprite.py +31 -19
  28. pyglet/experimental/particles.py +13 -35
  29. pyglet/font/__init__.py +251 -85
  30. pyglet/font/base.py +116 -61
  31. pyglet/font/dwrite/__init__.py +349 -204
  32. pyglet/font/dwrite/dwrite_lib.py +27 -5
  33. pyglet/font/fontconfig.py +14 -6
  34. pyglet/font/freetype.py +138 -87
  35. pyglet/font/freetype_lib.py +19 -0
  36. pyglet/font/group.py +179 -0
  37. pyglet/font/harfbuzz/__init__.py +3 -3
  38. pyglet/font/pyodide_js.py +310 -0
  39. pyglet/font/quartz.py +319 -126
  40. pyglet/font/ttf.py +45 -3
  41. pyglet/font/user.py +14 -19
  42. pyglet/font/win32.py +45 -21
  43. pyglet/graphics/__init__.py +8 -787
  44. pyglet/graphics/allocation.py +115 -1
  45. pyglet/graphics/api/__init__.py +77 -0
  46. pyglet/graphics/api/base.py +299 -0
  47. pyglet/graphics/api/gl/__init__.py +58 -0
  48. pyglet/graphics/api/gl/base.py +24 -0
  49. pyglet/graphics/{vertexbuffer.py → api/gl/buffer.py} +104 -159
  50. pyglet/graphics/api/gl/cocoa/context.py +76 -0
  51. pyglet/graphics/api/gl/context.py +391 -0
  52. pyglet/graphics/api/gl/default_shaders.py +0 -0
  53. pyglet/graphics/api/gl/draw.py +627 -0
  54. pyglet/graphics/api/gl/egl/__init__.py +0 -0
  55. pyglet/graphics/api/gl/egl/context.py +92 -0
  56. pyglet/graphics/api/gl/enums.py +76 -0
  57. pyglet/graphics/api/gl/framebuffer.py +315 -0
  58. pyglet/graphics/api/gl/gl.py +5463 -0
  59. pyglet/graphics/api/gl/gl_info.py +188 -0
  60. pyglet/graphics/api/gl/global_opengl.py +226 -0
  61. pyglet/{gl → graphics/api/gl}/lib.py +34 -18
  62. pyglet/graphics/api/gl/shader.py +1476 -0
  63. pyglet/graphics/api/gl/shapes.py +55 -0
  64. pyglet/graphics/api/gl/sprite.py +102 -0
  65. pyglet/graphics/api/gl/state.py +219 -0
  66. pyglet/graphics/api/gl/text.py +190 -0
  67. pyglet/graphics/api/gl/texture.py +1526 -0
  68. pyglet/graphics/{vertexarray.py → api/gl/vertexarray.py} +11 -13
  69. pyglet/graphics/api/gl/vertexdomain.py +751 -0
  70. pyglet/graphics/api/gl/win32/__init__.py +0 -0
  71. pyglet/graphics/api/gl/win32/context.py +108 -0
  72. pyglet/graphics/api/gl/win32/wgl_info.py +24 -0
  73. pyglet/graphics/api/gl/xlib/__init__.py +0 -0
  74. pyglet/graphics/api/gl/xlib/context.py +174 -0
  75. pyglet/{gl → graphics/api/gl/xlib}/glx_info.py +26 -31
  76. pyglet/graphics/api/gl1/__init__.py +0 -0
  77. pyglet/{gl → graphics/api/gl1}/gl_compat.py +3 -2
  78. pyglet/graphics/api/gl2/__init__.py +0 -0
  79. pyglet/graphics/api/gl2/buffer.py +320 -0
  80. pyglet/graphics/api/gl2/draw.py +600 -0
  81. pyglet/graphics/api/gl2/global_opengl.py +122 -0
  82. pyglet/graphics/api/gl2/shader.py +200 -0
  83. pyglet/graphics/api/gl2/shapes.py +51 -0
  84. pyglet/graphics/api/gl2/sprite.py +79 -0
  85. pyglet/graphics/api/gl2/text.py +175 -0
  86. pyglet/graphics/api/gl2/vertexdomain.py +364 -0
  87. pyglet/graphics/api/webgl/__init__.py +233 -0
  88. pyglet/graphics/api/webgl/buffer.py +302 -0
  89. pyglet/graphics/api/webgl/context.py +234 -0
  90. pyglet/graphics/api/webgl/draw.py +590 -0
  91. pyglet/graphics/api/webgl/enums.py +76 -0
  92. pyglet/graphics/api/webgl/framebuffer.py +360 -0
  93. pyglet/graphics/api/webgl/gl.py +1537 -0
  94. pyglet/graphics/api/webgl/gl_info.py +130 -0
  95. pyglet/graphics/api/webgl/shader.py +1346 -0
  96. pyglet/graphics/api/webgl/shapes.py +92 -0
  97. pyglet/graphics/api/webgl/sprite.py +102 -0
  98. pyglet/graphics/api/webgl/state.py +227 -0
  99. pyglet/graphics/api/webgl/text.py +187 -0
  100. pyglet/graphics/api/webgl/texture.py +1227 -0
  101. pyglet/graphics/api/webgl/vertexarray.py +54 -0
  102. pyglet/graphics/api/webgl/vertexdomain.py +616 -0
  103. pyglet/graphics/api/webgl/webgl_js.pyi +307 -0
  104. pyglet/{image → graphics}/atlas.py +33 -32
  105. pyglet/graphics/base.py +10 -0
  106. pyglet/graphics/buffer.py +245 -0
  107. pyglet/graphics/draw.py +578 -0
  108. pyglet/graphics/framebuffer.py +26 -0
  109. pyglet/graphics/instance.py +178 -69
  110. pyglet/graphics/shader.py +267 -1553
  111. pyglet/graphics/state.py +83 -0
  112. pyglet/graphics/texture.py +703 -0
  113. pyglet/graphics/vertexdomain.py +695 -538
  114. pyglet/gui/ninepatch.py +10 -10
  115. pyglet/gui/widgets.py +120 -10
  116. pyglet/image/__init__.py +20 -1973
  117. pyglet/image/animation.py +12 -12
  118. pyglet/image/base.py +730 -0
  119. pyglet/image/codecs/__init__.py +9 -0
  120. pyglet/image/codecs/bmp.py +53 -30
  121. pyglet/image/codecs/dds.py +53 -31
  122. pyglet/image/codecs/gdiplus.py +38 -14
  123. pyglet/image/codecs/gdkpixbuf2.py +0 -2
  124. pyglet/image/codecs/js_image.py +99 -0
  125. pyglet/image/codecs/ktx2.py +161 -0
  126. pyglet/image/codecs/pil.py +1 -1
  127. pyglet/image/codecs/png.py +1 -1
  128. pyglet/image/codecs/wic.py +11 -2
  129. pyglet/info.py +26 -24
  130. pyglet/input/__init__.py +8 -0
  131. pyglet/input/base.py +163 -105
  132. pyglet/input/controller.py +13 -19
  133. pyglet/input/controller_db.py +39 -24
  134. pyglet/input/emscripten/__init__.py +18 -0
  135. pyglet/input/emscripten/gamepad_js.py +397 -0
  136. pyglet/input/linux/__init__.py +11 -5
  137. pyglet/input/linux/evdev.py +10 -11
  138. pyglet/input/linux/x11_xinput.py +2 -2
  139. pyglet/input/linux/x11_xinput_tablet.py +1 -1
  140. pyglet/input/macos/__init__.py +7 -2
  141. pyglet/input/macos/darwin_gc.py +559 -0
  142. pyglet/input/win32/__init__.py +1 -1
  143. pyglet/input/win32/directinput.py +34 -29
  144. pyglet/input/win32/xinput.py +11 -61
  145. pyglet/lib.py +3 -3
  146. pyglet/libs/__init__.py +1 -1
  147. pyglet/{gl → libs/darwin}/agl.py +1 -1
  148. pyglet/libs/darwin/cocoapy/__init__.py +2 -2
  149. pyglet/libs/darwin/cocoapy/cocoahelpers.py +181 -0
  150. pyglet/libs/darwin/cocoapy/cocoalibs.py +31 -0
  151. pyglet/libs/darwin/cocoapy/cocoatypes.py +27 -0
  152. pyglet/libs/darwin/cocoapy/runtime.py +81 -45
  153. pyglet/libs/darwin/coreaudio.py +4 -4
  154. pyglet/{gl → libs/darwin}/lib_agl.py +9 -8
  155. pyglet/libs/darwin/quartzkey.py +1 -3
  156. pyglet/libs/egl/__init__.py +2 -0
  157. pyglet/libs/egl/egl_lib.py +576 -0
  158. pyglet/libs/egl/eglext.py +51 -5
  159. pyglet/libs/linux/__init__.py +0 -0
  160. pyglet/libs/linux/egl/__init__.py +0 -0
  161. pyglet/libs/linux/egl/eglext.py +22 -0
  162. pyglet/libs/linux/glx/__init__.py +0 -0
  163. pyglet/{gl → libs/linux/glx}/glx.py +13 -14
  164. pyglet/{gl → libs/linux/glx}/glxext_arb.py +408 -192
  165. pyglet/{gl → libs/linux/glx}/glxext_mesa.py +1 -1
  166. pyglet/{gl → libs/linux/glx}/glxext_nv.py +345 -164
  167. pyglet/{gl → libs/linux/glx}/lib_glx.py +3 -2
  168. pyglet/libs/linux/wayland/__init__.py +0 -0
  169. pyglet/libs/linux/wayland/client.py +1068 -0
  170. pyglet/libs/linux/wayland/lib_wayland.py +207 -0
  171. pyglet/libs/linux/wayland/wayland_egl.py +38 -0
  172. pyglet/libs/{wayland → linux/wayland}/xkbcommon.py +26 -0
  173. pyglet/libs/{x11 → linux/x11}/xf86vmode.py +4 -4
  174. pyglet/libs/{x11 → linux/x11}/xinerama.py +2 -2
  175. pyglet/libs/{x11 → linux/x11}/xinput.py +10 -10
  176. pyglet/libs/linux/x11/xrandr.py +0 -0
  177. pyglet/libs/{x11 → linux/x11}/xrender.py +1 -1
  178. pyglet/libs/shared/__init__.py +0 -0
  179. pyglet/libs/shared/spirv/__init__.py +0 -0
  180. pyglet/libs/shared/spirv/lib_shaderc.py +85 -0
  181. pyglet/libs/shared/spirv/lib_spirv_cross.py +126 -0
  182. pyglet/libs/win32/__init__.py +27 -5
  183. pyglet/libs/win32/constants.py +59 -48
  184. pyglet/libs/win32/context_managers.py +20 -3
  185. pyglet/libs/win32/dinput.py +105 -88
  186. pyglet/{gl → libs/win32}/lib_wgl.py +52 -26
  187. pyglet/libs/win32/types.py +58 -23
  188. pyglet/{gl → libs/win32}/wgl.py +32 -25
  189. pyglet/{gl → libs/win32}/wglext_arb.py +364 -2
  190. pyglet/media/__init__.py +9 -10
  191. pyglet/media/codecs/__init__.py +12 -1
  192. pyglet/media/codecs/base.py +99 -96
  193. pyglet/media/codecs/ffmpeg.py +2 -2
  194. pyglet/media/codecs/ffmpeg_lib/libavformat.py +3 -8
  195. pyglet/media/codecs/webaudio_pyodide.py +111 -0
  196. pyglet/media/drivers/__init__.py +9 -4
  197. pyglet/media/drivers/base.py +4 -4
  198. pyglet/media/drivers/openal/__init__.py +1 -1
  199. pyglet/media/drivers/openal/adaptation.py +3 -3
  200. pyglet/media/drivers/pulse/__init__.py +1 -1
  201. pyglet/media/drivers/pulse/adaptation.py +3 -3
  202. pyglet/media/drivers/pyodide_js/__init__.py +8 -0
  203. pyglet/media/drivers/pyodide_js/adaptation.py +288 -0
  204. pyglet/media/drivers/xaudio2/adaptation.py +3 -3
  205. pyglet/media/player.py +276 -193
  206. pyglet/media/player_worker_thread.py +1 -1
  207. pyglet/model/__init__.py +39 -29
  208. pyglet/model/codecs/base.py +4 -4
  209. pyglet/model/codecs/gltf.py +3 -3
  210. pyglet/model/codecs/obj.py +71 -43
  211. pyglet/resource.py +129 -78
  212. pyglet/shapes.py +147 -177
  213. pyglet/sprite.py +47 -164
  214. pyglet/text/__init__.py +44 -54
  215. pyglet/text/caret.py +12 -7
  216. pyglet/text/document.py +19 -17
  217. pyglet/text/formats/html.py +2 -2
  218. pyglet/text/formats/structured.py +10 -40
  219. pyglet/text/layout/__init__.py +20 -13
  220. pyglet/text/layout/base.py +176 -287
  221. pyglet/text/layout/incremental.py +9 -10
  222. pyglet/text/layout/scrolling.py +7 -95
  223. pyglet/window/__init__.py +183 -172
  224. pyglet/window/cocoa/__init__.py +62 -51
  225. pyglet/window/cocoa/pyglet_delegate.py +2 -25
  226. pyglet/window/cocoa/pyglet_view.py +9 -8
  227. pyglet/window/dialog/__init__.py +184 -0
  228. pyglet/window/dialog/base.py +99 -0
  229. pyglet/window/dialog/darwin.py +121 -0
  230. pyglet/window/dialog/linux.py +72 -0
  231. pyglet/window/dialog/windows.py +194 -0
  232. pyglet/window/emscripten/__init__.py +779 -0
  233. pyglet/window/headless/__init__.py +44 -28
  234. pyglet/window/key.py +2 -0
  235. pyglet/window/mouse.py +2 -2
  236. pyglet/window/wayland/__init__.py +377 -0
  237. pyglet/window/win32/__init__.py +101 -46
  238. pyglet/window/xlib/__init__.py +104 -66
  239. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/METADATA +2 -3
  240. pyglet-3.0.dev1.dist-info/RECORD +322 -0
  241. {pyglet-2.1.12.dist-info → pyglet-3.0.dev1.dist-info}/WHEEL +1 -1
  242. pyglet/gl/__init__.py +0 -208
  243. pyglet/gl/base.py +0 -499
  244. pyglet/gl/cocoa.py +0 -309
  245. pyglet/gl/gl.py +0 -4625
  246. pyglet/gl/gl.pyi +0 -2320
  247. pyglet/gl/gl_compat.pyi +0 -3097
  248. pyglet/gl/gl_info.py +0 -190
  249. pyglet/gl/headless.py +0 -166
  250. pyglet/gl/wgl_info.py +0 -36
  251. pyglet/gl/wglext_nv.py +0 -1096
  252. pyglet/gl/win32.py +0 -268
  253. pyglet/gl/xlib.py +0 -295
  254. pyglet/image/buffer.py +0 -274
  255. pyglet/image/codecs/s3tc.py +0 -354
  256. pyglet/libs/x11/xrandr.py +0 -166
  257. pyglet-2.1.12.dist-info/RECORD +0 -234
  258. /pyglet/{libs/wayland → graphics/api/gl/cocoa}/__init__.py +0 -0
  259. /pyglet/libs/{egl → linux/egl}/egl.py +0 -0
  260. /pyglet/libs/{egl → linux/egl}/lib.py +0 -0
  261. /pyglet/libs/{ioctl.py → linux/ioctl.py} +0 -0
  262. /pyglet/libs/{wayland → linux/wayland}/gbm.py +0 -0
  263. /pyglet/libs/{x11 → linux/x11}/__init__.py +0 -0
  264. /pyglet/libs/{x11 → linux/x11}/cursorfont.py +0 -0
  265. /pyglet/libs/{x11 → linux/x11}/xlib.py +0 -0
  266. /pyglet/libs/{x11 → linux/x11}/xsync.py +0 -0
  267. {pyglet-2.1.12.dist-info/licenses → pyglet-3.0.dev1.dist-info}/LICENSE +0 -0
@@ -3,16 +3,16 @@ from __future__ import annotations
3
3
  import ctypes
4
4
  import io
5
5
  from dataclasses import dataclass
6
- from typing import TYPE_CHECKING, BinaryIO, List, Optional, Union
6
+ from typing import TYPE_CHECKING, BinaryIO, Optional, Union
7
7
 
8
- from pyglet.media.exceptions import CannotSeekException, MediaException
8
+ from pyglet.media.exceptions import MediaException, CannotSeekException
9
9
 
10
10
  if TYPE_CHECKING:
11
- from pyglet.image import AbstractImage
11
+ from pyglet.graphics import Texture
12
12
  from pyglet.image.animation import Animation
13
13
  from pyglet.media.codecs import MediaEncoder
14
14
  from pyglet.media.drivers.base import MediaEvent
15
- from pyglet.media.player import Player
15
+ from pyglet.media.player import AudioPlayer
16
16
 
17
17
 
18
18
  class AudioFormat:
@@ -21,15 +21,20 @@ class AudioFormat:
21
21
  An instance of this class is provided by sources with audio tracks. You
22
22
  should not modify the fields, as they are used internally to describe the
23
23
  format of data provided by the source.
24
-
25
- Args:
26
- channels (int): The number of channels: 1 for mono or 2 for stereo
27
- (pyglet does not yet support surround-sound sources).
28
- sample_size (int): Bits per sample; only 8 or 16 are supported.
29
- sample_rate (int): Samples per second (in Hertz).
30
24
  """
31
25
 
32
26
  def __init__(self, channels: int, sample_size: int, sample_rate: int) -> None:
27
+ """Specify the audio format properties.
28
+
29
+ Args:
30
+ channels:
31
+ The number of channels: 1 for mono or 2 for stereo
32
+ (pyglet does not yet support surround-sound sources).
33
+ sample_size:
34
+ Bits per sample; only 8 or 16 are supported.
35
+ sample_rate:
36
+ Samples per second (in Hertz).
37
+ """
33
38
  self.channels = channels
34
39
  self.sample_size = sample_size
35
40
  self.sample_rate = sample_rate
@@ -91,12 +96,15 @@ class VideoFormat:
91
96
  should be displayed at 1280x480. It is the responsibility of the
92
97
  application to perform this scaling.
93
98
 
94
- Args:
95
- width (int): Width of video image, in pixels.
96
- height (int): Height of video image, in pixels.
97
- sample_aspect (float): Aspect ratio (width over height) of a single
98
- video pixel.
99
- frame_rate (float): Frame rate (frames per second) of the video.
99
+ Args:
100
+ width:
101
+ Width of video image, in pixels.
102
+ height:
103
+ Height of video image, in pixels.
104
+ sample_aspect:
105
+ Aspect ratio (width over height) of a single video pixel.
106
+ frame_rate:
107
+ Frame rate (frames per second) of the video or ``None`` if not known.
100
108
 
101
109
  .. versionadded:: 1.2
102
110
  """
@@ -110,29 +118,35 @@ class AudioData:
110
118
  """A single packet of audio data.
111
119
 
112
120
  This class is used internally by pyglet.
113
-
114
- Args:
115
- data (bytes, ctypes array, or supporting buffer protocol): Sample data.
116
- length (int): Size of sample data, in bytes.
117
- timestamp (float): Time of the first sample, in seconds.
118
- duration (float): Total data duration, in seconds.
119
- events (List[:class:`pyglet.media.drivers.base.MediaEvent`]): List of events
120
- contained within this packet. Events are timestamped relative to
121
- this audio packet.
122
-
123
- .. deprecated:: 2.0.10
124
- `timestamp` and `duration` are unused and will be removed eventually.
125
121
  """
126
122
 
127
123
  __slots__ = 'data', 'duration', 'events', 'length', 'pointer', 'timestamp'
128
124
 
129
- def __init__(self,
130
- data: bytes | ctypes.Array,
131
- length: int,
132
- timestamp: float = 0.0,
133
- duration: float = 0.0,
134
- events: list[MediaEvent] | None = None) -> None:
125
+ def __init__(
126
+ self,
127
+ data: bytes | ctypes.Array,
128
+ length: int,
129
+ timestamp: float = 0.0,
130
+ duration: float = 0.0,
131
+ events: list[MediaEvent] | None = None,
132
+ ) -> None:
133
+ """Events (List[:class:`pyglet.media.drivers.base.MediaEvent`]):
134
+
135
+ Args:
136
+ data:
137
+ (bytes, ctypes array, or supporting buffer protocol): Sample data.
138
+ length:
139
+ Size of sample data, in bytes.
140
+ timestamp:
141
+ Time of the first sample, in seconds.
142
+ duration:
143
+ Total data duration, in seconds.
144
+ events:
145
+ List of events contained within this packet. Events are timestamped relative to this audio packet.
135
146
 
147
+ .. deprecated:: 2.0.10
148
+ `timestamp` and `duration` are unused and will be removed eventually.
149
+ """
136
150
  if isinstance(data, bytes):
137
151
  # bytes are treated specially by ctypes and can be cast to a void pointer, get
138
152
  # their content's address like this
@@ -200,21 +214,21 @@ class Source:
200
214
  is_player_source (bool): Determine if this source is a player
201
215
  current source.
202
216
 
203
- Check on a :py:class:`~pyglet.media.player.Player` if this source
217
+ Check on a :py:class:`~pyglet.media.player.AudioPlayer` if this source
204
218
  is the current source.
205
219
  """
206
220
 
207
221
  _duration = None
208
- _players: List['Player'] = [] # Players created through Source.play
222
+ _players: list[AudioPlayer] = [] # Players created through Source.play
209
223
 
210
- audio_format = None
211
- video_format = None
212
- info = None
213
- is_player_source = False
224
+ audio_format: AudioFormat | None = None
225
+ video_format: VideoFormat | None = None
226
+ info: SourceInfo | None = None
227
+ is_player_source: bool = False
214
228
 
215
229
  @property
216
230
  def duration(self) -> float:
217
- """float: The length of the source, in seconds.
231
+ """The length of the source, in seconds.
218
232
 
219
233
  Not all source durations can be determined; in this case the value
220
234
  is ``None``.
@@ -223,7 +237,7 @@ class Source:
223
237
  """
224
238
  return self._duration
225
239
 
226
- def play(self) -> 'Player':
240
+ def play(self) -> AudioPlayer:
227
241
  """Play the source.
228
242
 
229
243
  This is a convenience method which creates a Player for
@@ -232,8 +246,9 @@ class Source:
232
246
  Returns:
233
247
  :class:`.Player`
234
248
  """
235
- from pyglet.media.player import Player # XXX Nasty circular dependency
236
- player = Player()
249
+ from pyglet.media.player import AudioPlayer # XXX Nasty circular dependency
250
+
251
+ player = AudioPlayer()
237
252
  player.queue(self)
238
253
  player.play()
239
254
  Source._players.append(player)
@@ -259,28 +274,25 @@ class Source:
259
274
  few seconds.
260
275
 
261
276
  .. versionadded:: 1.1
262
-
263
- Returns:
264
- :class:`pyglet.image.Animation`
265
277
  """
266
278
  from pyglet.image import Animation, AnimationFrame
279
+
267
280
  if not self.video_format:
268
281
  # XXX: This causes an assertion in the constructor of Animation
269
282
  return Animation([])
270
- else:
271
- frames = []
272
- last_ts = 0
283
+ frames = []
284
+ last_ts = 0
285
+ next_ts = self.get_next_video_timestamp()
286
+ while next_ts is not None:
287
+ image = self.get_next_video_frame()
288
+ if image is not None:
289
+ delay = next_ts - last_ts
290
+ frames.append(AnimationFrame(image, delay))
291
+ last_ts = next_ts
273
292
  next_ts = self.get_next_video_timestamp()
274
- while next_ts is not None:
275
- image = self.get_next_video_frame()
276
- if image is not None:
277
- delay = next_ts - last_ts
278
- frames.append(AnimationFrame(image, delay))
279
- last_ts = next_ts
280
- next_ts = self.get_next_video_timestamp()
281
- return Animation(frames)
282
-
283
- def get_next_video_timestamp(self) -> Optional[float]:
293
+ return Animation(frames)
294
+
295
+ def get_next_video_timestamp(self) -> float | None:
284
296
  """Get the timestamp of the next video frame.
285
297
 
286
298
  .. versionadded:: 1.1
@@ -289,33 +301,27 @@ class Source:
289
301
  float: The next timestamp, or ``None`` if there are no more video
290
302
  frames.
291
303
  """
292
- pass
293
304
 
294
- def get_next_video_frame(self) -> Optional['AbstractImage']:
305
+ def get_next_video_frame(self) -> Texture | None:
295
306
  """Get the next video frame.
296
307
 
297
- .. versionadded:: 1.1
298
-
299
308
  Returns:
300
- :class:`pyglet.image.AbstractImage`: The next video frame image,
301
- or ``None`` if the video frame could not be decoded or there are
309
+ The next video frame image, or ``None`` if the video frame could not be decoded or there are
302
310
  no more video frames.
311
+
312
+ .. versionadded:: 1.1
303
313
  """
304
- pass
305
314
 
306
- def save(self,
307
- filename: str,
308
- file: Optional[BinaryIO] = None,
309
- encoder: Optional['MediaEncoder'] = None) -> None:
315
+ def save(self, filename: str, file: BinaryIO | None = None, encoder: MediaEncoder | None = None) -> None:
310
316
  """Save this Source to a file.
311
317
 
312
- :Parameters:
313
- `filename` : str
318
+ Args:
319
+ filename
314
320
  Used to set the file format, and to open the output file
315
321
  if `file` is unspecified.
316
- `file` : file-like object or None
322
+ file:
317
323
  File to write audio data to.
318
- `encoder` : MediaEncoder or None
324
+ encoder:
319
325
  If unspecified, all encoders matching the filename extension
320
326
  are tried. If all fail, the exception from the first one
321
327
  attempted is raised.
@@ -323,14 +329,14 @@ class Source:
323
329
  """
324
330
  if encoder:
325
331
  return encoder.encode(self, filename, file)
326
- else:
327
- import pyglet.media.codecs
328
- return pyglet.media.codecs.registry.encode(self, filename, file)
332
+ import pyglet.media.codecs
333
+
334
+ return pyglet.media.codecs.registry.encode(self, filename, file)
329
335
 
330
336
  # Internal methods that Player calls on the source:
331
337
 
332
338
  def is_precise(self) -> bool:
333
- """bool: Whether this source is considered precise.
339
+ """Whether this source is considered precise.
334
340
 
335
341
  ``x`` bytes on source ``s`` are considered aligned if
336
342
  ``x % s.audio_format.bytes_per_frame == 0``, so there'd be no partial
@@ -362,8 +368,8 @@ class Source:
362
368
  negatively impacted at best and memory access violations occur at
363
369
  worst.
364
370
 
365
- :Returns:
366
- bool: Whether the source is precise.
371
+ Returns:
372
+ Whether the source is precise.
367
373
  """
368
374
  return False
369
375
 
@@ -376,7 +382,7 @@ class Source:
376
382
  """
377
383
  raise CannotSeekException()
378
384
 
379
- def get_queue_source(self) -> 'Source':
385
+ def get_queue_source(self) -> Source:
380
386
  """Return the ``Source`` to be used as the queue source for a player.
381
387
 
382
388
  Default implementation returns ``self``.
@@ -386,7 +392,7 @@ class Source:
386
392
  """
387
393
  return self
388
394
 
389
- def get_audio_data(self, num_bytes: int, compensation_time=0.0) -> Optional[AudioData]:
395
+ def get_audio_data(self, num_bytes: int, compensation_time=0.0) -> AudioData | None:
390
396
  """Get next packet of audio data.
391
397
 
392
398
  Args:
@@ -409,10 +415,10 @@ class StreamingSource(Source):
409
415
  """A source that is decoded as it is being played.
410
416
 
411
417
  The source can only be played once at a time on any
412
- :class:`~pyglet.media.player.Player`.
418
+ :class:`~pyglet.media.player.AudioPlayer`.
413
419
  """
414
420
 
415
- def get_queue_source(self) -> 'StreamingSource':
421
+ def get_queue_source(self) -> StreamingSource:
416
422
  """Return the ``Source`` to be used as the source for a player.
417
423
 
418
424
  Default implementation returns self.
@@ -427,7 +433,6 @@ class StreamingSource(Source):
427
433
 
428
434
  def delete(self) -> None:
429
435
  """Release the resources held by this StreamingSource."""
430
- pass
431
436
 
432
437
 
433
438
  class StaticSource(Source):
@@ -469,7 +474,7 @@ class StaticSource(Source):
469
474
 
470
475
  self._duration = len(self._data) / self.audio_format.bytes_per_second
471
476
 
472
- def get_queue_source(self) -> Optional['StaticMemorySource']:
477
+ def get_queue_source(self) -> Optional[StaticMemorySource]:
473
478
  if self._data is not None:
474
479
  return StaticMemorySource(self._data, self.audio_format)
475
480
  return None
@@ -478,7 +483,7 @@ class StaticSource(Source):
478
483
  """The StaticSource does not provide audio data.
479
484
 
480
485
  When the StaticSource is queued on a
481
- :class:`~pyglet.media.player.Player`, it creates a
486
+ :class:`~pyglet.media.player.AudioPlayer`, it creates a
482
487
  :class:`.StaticMemorySource` containing its internal audio data and
483
488
  audio format.
484
489
 
@@ -489,8 +494,7 @@ class StaticSource(Source):
489
494
 
490
495
 
491
496
  class StaticMemorySource(StaticSource):
492
- """
493
- Helper class for default implementation of :class:`.StaticSource`.
497
+ """Helper class for default implementation of :class:`.StaticSource`.
494
498
 
495
499
  Do not use directly. This class is used internally by pyglet.
496
500
 
@@ -569,14 +573,14 @@ class SourceGroup:
569
573
  self.audio_format = self.audio_format or source.audio_format
570
574
  self.info = self.info or source.info
571
575
  source = source.get_queue_source()
572
- assert (source.audio_format == self.audio_format), "Sources must share the same audio format."
576
+ assert source.audio_format == self.audio_format, "Sources must share the same audio format."
573
577
  self._sources.append(source)
574
578
  self.duration += source.duration
575
579
 
576
580
  def has_next(self) -> bool:
577
581
  return len(self._sources) > 1
578
582
 
579
- def get_queue_source(self) -> 'SourceGroup':
583
+ def get_queue_source(self) -> SourceGroup:
580
584
  return self
581
585
 
582
586
  def _advance(self) -> None:
@@ -592,14 +596,13 @@ class SourceGroup:
592
596
  def get_audio_data(self, num_bytes: float, compensation_time=0.0) -> Optional[AudioData]:
593
597
  """Get next audio packet.
594
598
 
595
- :Parameters:
599
+ Args:
596
600
  `num_bytes` : int
597
601
  Hint for preferred size of audio packet; may be ignored.
598
602
 
599
- :rtype: `AudioData`
600
- :return: Audio data, or None if there is no more data.
603
+ Returns:
604
+ Audio data, or ``None`` if there is no more data.
601
605
  """
602
-
603
606
  if not self._sources:
604
607
  return None
605
608
 
@@ -1037,7 +1037,7 @@ class FFmpegSource(StreamingSource):
1037
1037
  width = self.video_format.width
1038
1038
  height = self.video_format.height
1039
1039
  pitch = width * 4
1040
- buf_size = avutil.av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 1) + AV_INPUT_BUFFER_PADDING_SIZE
1040
+ buf_size = avutil.av_image_get_buffer_size(AV_PIX_FMT_RGBA, width, height, 1)
1041
1041
  buffer = (c_uint8 * buf_size)()
1042
1042
  try:
1043
1043
  result = self._ffmpeg_decode_video(video_packet.packet, buffer)
@@ -1195,7 +1195,7 @@ class FFmpegSource(StreamingSource):
1195
1195
 
1196
1196
 
1197
1197
  ffmpeg_init()
1198
- if pyglet.options['debug_media']:
1198
+ if pyglet.options.debug_media:
1199
1199
  _debug = True
1200
1200
  else:
1201
1201
  _debug = False
@@ -132,8 +132,8 @@ AVStream_Fields = [
132
132
  ('metadata', POINTER(AVDictionary)),
133
133
  ('avg_frame_rate', AVRational),
134
134
  ('attached_pic', AVPacket),
135
- ('side_data', POINTER(AVPacketSideData)), # Deprecated in 60. Removed in 62.
136
- ('nb_side_data', c_int), # Deprecated in 60. Removed in 62.
135
+ ('side_data', POINTER(AVPacketSideData)),
136
+ ('nb_side_data', c_int),
137
137
  ('event_flags', c_int),
138
138
  ('r_frame_rate', AVRational),
139
139
  ('recommended_encoder_configuration', c_char_p), # Deprecated. Removed in 59.
@@ -147,16 +147,11 @@ compat.add_version_changes('avformat', 58, AVStream, AVStream_Fields, removals=(
147
147
  compat.add_version_changes('avformat', 59, AVStream, AVStream_Fields,
148
148
  removals=('av_class', 'codec', 'recommended_encoder_configuration', 'info'))
149
149
 
150
- for compat_ver in (60, 61):
150
+ for compat_ver in (60, 61, 62):
151
151
  compat.add_version_changes('avformat', compat_ver, AVStream, AVStream_Fields,
152
152
  removals=('codec', 'recommended_encoder_configuration', 'info'),
153
153
  repositions=(compat.Reposition("codecpar", "id"),))
154
154
 
155
- compat.add_version_changes('avformat', 62, AVStream, AVStream_Fields,
156
- removals=('codec', 'recommended_encoder_configuration', 'info', 'side_data',
157
- 'nb_side_data'),
158
- repositions=(compat.Reposition("codecpar", "id"),))
159
-
160
155
 
161
156
  class AVProgram(Structure):
162
157
  pass
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import weakref
4
+ from typing import BinaryIO, NoReturn, TYPE_CHECKING
5
+
6
+ import pyglet
7
+
8
+ from . import MediaDecoder
9
+ from .base import AudioFormat, Source, StaticSource
10
+
11
+ _debug = pyglet.options.debug_media
12
+
13
+ try:
14
+ import js
15
+ import pyodide.ffi
16
+ except ImportError:
17
+ raise ImportError("Pyodide not found.")
18
+
19
+
20
+ from pyglet.media import get_audio_driver
21
+
22
+ if TYPE_CHECKING:
23
+ from pyglet.media.drivers.pyodide_js.adaptation import PyodideJSAudioPlayer
24
+
25
+
26
+ class JavascriptWebAudioSource(Source):
27
+ def __init__(self, filename: str, file: BinaryIO | None = None) -> None: # noqa: D107
28
+ if file is None:
29
+ with open(filename, 'rb') as f:
30
+ data = f.read()
31
+ self._file = data
32
+ else:
33
+ self._file = file.read()
34
+
35
+ self.filename = filename
36
+ self.js_array = js.Uint8Array.new(self._file)
37
+ self.audio_buffer = None
38
+ self._duration = 0
39
+ self._current_offset = 0
40
+
41
+ # Default dummy audio format so AudioPlayer can create an internal PyodideJSAudioPlayer.
42
+ # The JavaScript internal player doesn't use this object.
43
+ self.audio_format = AudioFormat(channels=2, sample_size=16, sample_rate=44100)
44
+
45
+ # If this audio is not finished loading, a player may be waiting to play it.
46
+ self._waiting_players = weakref.WeakSet()
47
+
48
+ get_audio_driver().decode_audio(self.js_array.buffer).then(self.on_decode).catch(self.on_error)
49
+
50
+ def add_player(self, player: PyodideJSAudioPlayer) -> None:
51
+ self._waiting_players.add(player)
52
+
53
+ # Decode the audio data
54
+ def on_decode(self, audio_buffer) -> None:
55
+ if _debug:
56
+ print(f"Decoded {self.filename} successfully.")
57
+ self.audio_buffer = audio_buffer
58
+
59
+ self.audio_format = AudioFormat(
60
+ channels=self.audio_buffer.numberOfChannels,
61
+ sample_size=16,
62
+ sample_rate=self.audio_buffer.sampleRate,
63
+ )
64
+ self._duration = self.audio_buffer.duration
65
+
66
+ for player in self._waiting_players:
67
+ player.on_source_finished_loading(self)
68
+
69
+ self._waiting_players.clear()
70
+
71
+ def on_error(self, error) -> NoReturn:
72
+ js.console.log(f"Failed decoding {self.filename}: {error}")
73
+
74
+ self._waiting_players.clear()
75
+
76
+ def __del__(self) -> None:
77
+ if hasattr(self, '_file'):
78
+ self._file = None
79
+
80
+ self.js_array = None
81
+ self.audio_buffer = None
82
+
83
+ def get_audio_data(self, num_bytes: int, compensation_time: float = 0.0) -> NoReturn:
84
+ raise NotImplementedError("This is not supported and should not be called.")
85
+
86
+ def seek(self, timestamp: float) -> NoReturn:
87
+ """This is not supported by Javascript."""
88
+
89
+
90
+ #########################################
91
+ # Decoder class:
92
+ #########################################
93
+
94
+
95
+ class PyodideDecoder(MediaDecoder): # noqa: D101
96
+ def get_file_extensions(self) -> tuple[str, ...]:
97
+ return '.mp3', '.aac', '.wav', '.ogg', '.webm'
98
+ # possibly use audio.canPlayType?
99
+
100
+ def decode(self, filename: str, file: BinaryIO, streaming: bool = True) -> JavascriptWebAudioSource | StaticSource:
101
+ if streaming:
102
+ return JavascriptWebAudioSource(filename, file)
103
+ return StaticSource(JavascriptWebAudioSource(filename, file))
104
+
105
+
106
+ def get_decoders() -> list[PyodideDecoder]: # noqa: D103
107
+ return [PyodideDecoder()]
108
+
109
+
110
+ def get_encoders() -> list: # noqa: D103
111
+ return []
@@ -5,7 +5,7 @@ import atexit
5
5
 
6
6
  import pyglet
7
7
 
8
- _debug = pyglet.options['debug_media']
8
+ _debug = pyglet.options.debug_media
9
9
  _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
10
10
 
11
11
 
@@ -15,7 +15,7 @@ if _is_pyglet_doc_run:
15
15
 
16
16
  else:
17
17
 
18
- for driver_name in pyglet.options['audio']:
18
+ for driver_name in pyglet.options.audio:
19
19
  try:
20
20
  if driver_name == 'pulse':
21
21
  from . import pulse
@@ -55,8 +55,13 @@ else:
55
55
  traceback.print_exc()
56
56
 
57
57
  else:
58
- from . import silent
59
- _audio_driver = silent.create_audio_driver()
58
+ if pyglet.compat_platform == "emscripten":
59
+ from . import pyodide_js
60
+
61
+ _audio_driver = pyodide_js.create_audio_driver()
62
+ else:
63
+ from . import silent
64
+ _audio_driver = silent.create_audio_driver()
60
65
 
61
66
 
62
67
  def get_audio_driver():
@@ -409,7 +409,7 @@ class AbstractAudioPlayer(metaclass=ABCMeta):
409
409
  pass
410
410
 
411
411
  def set_position(self, position):
412
- """See :py:attr:`~pyglet.media.Player.position`."""
412
+ """See :py:attr:`~pyglet.media.AudioPlayer.position`."""
413
413
  pass
414
414
 
415
415
  def set_min_distance(self, min_distance):
@@ -421,7 +421,7 @@ class AbstractAudioPlayer(metaclass=ABCMeta):
421
421
  pass
422
422
 
423
423
  def set_pitch(self, pitch):
424
- """See :py:attr:`~pyglet.media.Player.pitch`."""
424
+ """See :py:attr:`~pyglet.media.AudioPlayer.pitch`."""
425
425
  pass
426
426
 
427
427
  def set_cone_orientation(self, cone_orientation):
@@ -459,7 +459,7 @@ class MediaEvent:
459
459
  """Representation of a media event.
460
460
 
461
461
  These events are used internally by some audio driver implementation to
462
- communicate events to the :class:`~pyglet.media.player.Player`.
462
+ communicate events to the :class:`~pyglet.media.player.AudioPlayer`.
463
463
  One example is the ``on_eos`` event.
464
464
 
465
465
  Args:
@@ -468,7 +468,7 @@ class MediaEvent:
468
468
  *args: Any required positional argument to go along with this event.
469
469
  """
470
470
 
471
- __slots__ = 'event', 'timestamp', 'args'
471
+ __slots__ = 'args', 'event', 'timestamp'
472
472
 
473
473
  def __init__(self, event, timestamp=0.0, *args):
474
474
  # Meaning of timestamp is dependent on context; and not seen by application.
@@ -2,7 +2,7 @@ from .adaptation import OpenALDriver
2
2
 
3
3
  import pyglet
4
4
 
5
- _debug = pyglet.options['debug_media']
5
+ _debug = pyglet.options.debug_media
6
6
  _debug_buffers = pyglet.options.get('debug_media_buffers', False)
7
7
 
8
8
 
@@ -9,7 +9,7 @@ from pyglet.media.player_worker_thread import PlayerWorkerThread
9
9
  from pyglet.util import debug_print
10
10
 
11
11
  if TYPE_CHECKING:
12
- from pyglet.media import Source, Player
12
+ from pyglet.media import Source, AudioPlayer
13
13
 
14
14
 
15
15
  _debug = debug_print('debug_media')
@@ -28,7 +28,7 @@ class OpenALDriver(AbstractAudioDriver):
28
28
  self.worker = PlayerWorkerThread()
29
29
  self.worker.start()
30
30
 
31
- def create_audio_player(self, source: 'Source', player: 'Player') -> 'OpenALAudioPlayer':
31
+ def create_audio_player(self, source: 'Source', player: 'AudioPlayer') -> 'OpenALAudioPlayer':
32
32
  assert self.device is not None, 'Device was closed'
33
33
  return OpenALAudioPlayer(self, source, player)
34
34
 
@@ -91,7 +91,7 @@ class OpenALListener(AbstractListener):
91
91
 
92
92
 
93
93
  class OpenALAudioPlayer(AbstractAudioPlayer):
94
- def __init__(self, driver: 'OpenALDriver', source: 'Source', player: 'Player') -> None:
94
+ def __init__(self, driver: 'OpenALDriver', source: 'Source', player: 'AudioPlayer') -> None:
95
95
  super().__init__(source, player)
96
96
  self.driver = driver
97
97
  self.alsource = driver.context.create_source()
@@ -2,7 +2,7 @@ from .adaptation import PulseAudioDriver
2
2
 
3
3
  import pyglet
4
4
 
5
- _debug = pyglet.options['debug_media']
5
+ _debug = pyglet.options.debug_media
6
6
 
7
7
 
8
8
  def create_audio_driver():