pyglet 2.1.5__py3-none-any.whl → 2.1.8__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 (71) hide show
  1. pyglet/__init__.py +27 -42
  2. pyglet/app/base.py +2 -2
  3. pyglet/clock.py +1 -1
  4. pyglet/display/base.py +31 -21
  5. pyglet/display/cocoa.py +25 -1
  6. pyglet/display/headless.py +1 -1
  7. pyglet/display/win32.py +134 -18
  8. pyglet/display/xlib.py +285 -70
  9. pyglet/event.py +17 -1
  10. pyglet/experimental/README.md +1 -1
  11. pyglet/experimental/jobs.py +1 -1
  12. pyglet/experimental/multitexture_sprite.py +2 -2
  13. pyglet/font/__init__.py +1 -1
  14. pyglet/font/base.py +8 -5
  15. pyglet/font/dwrite/__init__.py +13 -8
  16. pyglet/font/dwrite/dwrite_lib.py +1 -1
  17. pyglet/font/user.py +1 -1
  18. pyglet/gl/base.py +8 -4
  19. pyglet/gl/cocoa.py +4 -0
  20. pyglet/gl/gl.py +4 -3
  21. pyglet/gl/gl.pyi +2320 -0
  22. pyglet/gl/gl_compat.py +7 -18
  23. pyglet/gl/gl_compat.pyi +3097 -0
  24. pyglet/gl/xlib.py +24 -0
  25. pyglet/graphics/shader.py +34 -20
  26. pyglet/graphics/vertexbuffer.py +1 -1
  27. pyglet/gui/frame.py +2 -2
  28. pyglet/gui/widgets.py +1 -1
  29. pyglet/image/__init__.py +3 -3
  30. pyglet/image/buffer.py +3 -3
  31. pyglet/input/base.py +8 -8
  32. pyglet/input/linux/evdev.py +1 -1
  33. pyglet/libs/darwin/cocoapy/cocoalibs.py +3 -1
  34. pyglet/libs/win32/__init__.py +12 -0
  35. pyglet/libs/win32/constants.py +4 -0
  36. pyglet/libs/win32/types.py +97 -0
  37. pyglet/libs/x11/xrandr.py +166 -0
  38. pyglet/libs/x11/xrender.py +43 -0
  39. pyglet/libs/x11/xsync.py +43 -0
  40. pyglet/math.py +40 -49
  41. pyglet/media/buffered_logger.py +1 -1
  42. pyglet/media/codecs/ffmpeg.py +18 -34
  43. pyglet/media/codecs/gstreamer.py +3 -3
  44. pyglet/media/codecs/pyogg.py +1 -1
  45. pyglet/media/codecs/wave.py +6 -0
  46. pyglet/media/codecs/wmf.py +33 -7
  47. pyglet/media/devices/win32.py +1 -1
  48. pyglet/media/drivers/base.py +1 -1
  49. pyglet/media/drivers/directsound/interface.py +4 -0
  50. pyglet/media/drivers/listener.py +2 -2
  51. pyglet/media/drivers/xaudio2/interface.py +6 -2
  52. pyglet/media/drivers/xaudio2/lib_xaudio2.py +1 -1
  53. pyglet/media/instrumentation.py +2 -2
  54. pyglet/media/player.py +2 -2
  55. pyglet/media/player_worker_thread.py +1 -1
  56. pyglet/media/synthesis.py +1 -1
  57. pyglet/model/codecs/gltf.py +1 -1
  58. pyglet/shapes.py +25 -24
  59. pyglet/sprite.py +1 -1
  60. pyglet/text/caret.py +44 -5
  61. pyglet/text/layout/base.py +3 -3
  62. pyglet/util.py +1 -1
  63. pyglet/window/__init__.py +54 -14
  64. pyglet/window/cocoa/__init__.py +27 -0
  65. pyglet/window/mouse.py +11 -1
  66. pyglet/window/win32/__init__.py +40 -14
  67. pyglet/window/xlib/__init__.py +21 -7
  68. {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/METADATA +1 -1
  69. {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/RECORD +71 -67
  70. {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/LICENSE +0 -0
  71. {pyglet-2.1.5.dist-info → pyglet-2.1.8.dist-info}/WHEEL +0 -0
@@ -0,0 +1,166 @@
1
+ import warnings
2
+ from ctypes import c_ulong, c_int, POINTER, Structure, c_char_p, c_uint, c_ushort
3
+
4
+ import pyglet
5
+ from pyglet.libs.x11.xlib import (
6
+ Time,
7
+ Window,
8
+ Display,
9
+ XCloseDisplay,
10
+ XDefaultRootWindow,
11
+ XOpenDisplay,
12
+ )
13
+
14
+ lib = None
15
+ try:
16
+ lib = pyglet.lib.load_library("Xrandr")
17
+ except ImportError:
18
+ if pyglet.options.debug_lib:
19
+ warnings.warn("Xrandr could not be loaded.")
20
+ raise ImportError
21
+
22
+ RRCrtc = c_ulong # typedef XID
23
+ RROutput = c_ulong # typedef XID
24
+ RRMode = c_ulong # typedef XID
25
+ Connection = c_ushort # Connection in Xrandr.h
26
+ SubpixelOrder = c_ushort # SubpixelOrder in Xrandr.h
27
+ XRRModeFlags = c_ulong
28
+
29
+ class XRRModeInfo(Structure):
30
+ _fields_ = [
31
+ ("id", RRMode), # mode ID
32
+ ("width", c_uint), # horizontal resolution
33
+ ("height", c_uint), # vertical resolution
34
+ ("dotClock", c_ulong), # pixel clock (in Hz)
35
+ ("hSyncStart", c_uint),
36
+ ("hSyncEnd", c_uint),
37
+ ("hTotal", c_uint),
38
+ ("hSkew", c_uint),
39
+ ("vSyncStart", c_uint),
40
+ ("vSyncEnd", c_uint),
41
+ ("vTotal", c_uint),
42
+ ("name", c_char_p),
43
+ ("nameLength", c_uint),
44
+ ("modeFlags", XRRModeFlags),
45
+ ]
46
+
47
+
48
+ class XRRScreenResources(Structure):
49
+ _fields_ = [
50
+ ("timestamp", Time),
51
+ ("configTimestamp", Time),
52
+ ("ncrtc", c_int),
53
+ ("crtcs", POINTER(RRCrtc)),
54
+ ("noutput", c_int),
55
+ ("outputs", POINTER(RROutput)),
56
+ ("nmode", c_int),
57
+ ("modes", POINTER(XRRModeInfo)),
58
+ ]
59
+
60
+
61
+ class XRROutputInfo(Structure):
62
+ _fields_ = [
63
+ ("timestamp", Time),
64
+ ("crtc", RRCrtc),
65
+ ("name", c_char_p),
66
+ ("nameLen", c_int),
67
+ ("mm_width", c_ulong),
68
+ ("mm_height", c_ulong),
69
+ ("connection", Connection),
70
+ ("subpixel_order", SubpixelOrder),
71
+ ("ncrtc", c_int),
72
+ ("crtcs", POINTER(RRCrtc)),
73
+ ("nclone", c_int),
74
+ ("clones", POINTER(RROutput)),
75
+ ("nmode", c_int),
76
+ ("npreferred", c_int),
77
+ ("modes", POINTER(RRMode)),
78
+ ]
79
+
80
+ class XRRCrtcInfo(Structure):
81
+ _fields_ = [
82
+ ("timestamp", Time),
83
+ ("x", c_int),
84
+ ("y", c_int),
85
+ ("width", c_uint),
86
+ ("height", c_uint),
87
+ ("mode", RRMode),
88
+ ("rotation", c_int),
89
+ ("noutput", c_int),
90
+ ("outputs", POINTER(RROutput)),
91
+ ("rotations", c_ushort),
92
+ ("npossible", c_int),
93
+ ("possible", POINTER(RROutput)),
94
+ ]
95
+
96
+
97
+ if lib:
98
+ XRRQueryVersion = lib.XRRQueryVersion
99
+ XRRQueryVersion.argtypes = [POINTER(Display), POINTER(c_int), POINTER(c_int)]
100
+ XRRQueryVersion.restype = c_int
101
+
102
+ XRRGetScreenResources = lib.XRRGetScreenResources
103
+ XRRGetScreenResources.argtypes = [POINTER(Display), Window]
104
+ XRRGetScreenResources.restype = POINTER(XRRScreenResources)
105
+
106
+ XRRGetOutputPrimary = lib.XRRGetOutputPrimary
107
+ XRRGetOutputPrimary.argtypes = [POINTER(Display), Window]
108
+ XRRGetOutputPrimary.restype = RROutput
109
+
110
+ XRRGetScreenResourcesCurrent = lib.XRRGetScreenResourcesCurrent
111
+ XRRGetScreenResourcesCurrent.argtypes = [POINTER(Display), Window]
112
+ XRRGetScreenResourcesCurrent.restype = POINTER(XRRScreenResources)
113
+
114
+ XRRFreeScreenResources = lib.XRRFreeScreenResources
115
+ XRRFreeScreenResources.argtypes = [POINTER(XRRScreenResources)]
116
+ XRRFreeScreenResources.restype = None
117
+
118
+ XRRGetOutputInfo = lib.XRRGetOutputInfo
119
+ XRRGetOutputInfo.argtypes = [POINTER(Display), POINTER(XRRScreenResources), RROutput]
120
+ XRRGetOutputInfo.restype = POINTER(XRROutputInfo)
121
+
122
+ XRRFreeOutputInfo = lib.XRRFreeOutputInfo
123
+ XRRFreeOutputInfo.argtypes = [POINTER(XRROutputInfo)]
124
+ XRRFreeOutputInfo.restype = None
125
+
126
+ XRRGetCrtcInfo = lib.XRRGetCrtcInfo
127
+ XRRGetCrtcInfo.argtypes = [POINTER(Display), POINTER(XRRScreenResources), RRCrtc]
128
+ XRRGetCrtcInfo.restype = POINTER(XRRCrtcInfo)
129
+
130
+ XRRSetCrtcConfig = lib.XRRSetCrtcConfig
131
+ XRRSetCrtcConfig.argtypes = argtypes = [POINTER(Display), POINTER(XRRScreenResources), RRCrtc, Time, c_int, c_int, RRMode, c_int, POINTER(RROutput), c_int]
132
+ XRRSetCrtcConfig.restype = c_int
133
+
134
+ XRRFreeCrtcInfo = lib.XRRFreeCrtcInfo
135
+ XRRFreeCrtcInfo.argtypes = [POINTER(XRRCrtcInfo)]
136
+ XRRFreeCrtcInfo.restype = None
137
+
138
+ def list_connected_outputs():
139
+ dpy = XOpenDisplay(None)
140
+ if not dpy:
141
+ raise RuntimeError("Cannot open DISPLAY")
142
+
143
+ root = XDefaultRootWindow(dpy)
144
+ res_p = XRRGetScreenResources(dpy, root)
145
+ if not res_p:
146
+ XCloseDisplay(dpy)
147
+ raise RuntimeError("Failed to get screen resources")
148
+
149
+ res = res_p.contents
150
+ outputs = []
151
+ for i in range(res.noutput):
152
+ out_id = res.outputs[i]
153
+ info_p = XRRGetOutputInfo(dpy, res_p, out_id)
154
+ if info_p:
155
+ info = info_p.contents
156
+ if info.connection == 0: # Connected.
157
+ outputs.append(info.name.decode())
158
+ XRRFreeOutputInfo(info_p)
159
+
160
+ XRRFreeScreenResources(res_p)
161
+ XCloseDisplay(dpy)
162
+ return outputs
163
+
164
+ if __name__ == "__main__":
165
+ for name in list_connected_outputs():
166
+ print(name)
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ import warnings
4
+ from ctypes import Structure, c_ushort, c_ulong, c_int, c_void_p, POINTER
5
+ import pyglet.lib
6
+ from pyglet.libs.x11.xlib import Visual
7
+
8
+ lib = None
9
+ try:
10
+ lib = pyglet.lib.load_library("Xrender")
11
+ except ImportError:
12
+ if pyglet.options.debug_lib:
13
+ warnings.warn("XRender could not be loaded.")
14
+
15
+
16
+ class XRenderDirectFormat(Structure):
17
+ _fields_ = [
18
+ ('red', c_ushort),
19
+ ('redMask', c_ushort),
20
+ ('green', c_ushort),
21
+ ('greenMask', c_ushort),
22
+ ('blue', c_ushort),
23
+ ('blueMask', c_ushort),
24
+ ('alpha', c_ushort),
25
+ ('alphaMask', c_ushort),
26
+ ]
27
+
28
+ class XRenderPictFormat(Structure):
29
+ _fields_ = [
30
+ ('id', c_ulong),
31
+ ('type', c_int),
32
+ ('depth', c_int),
33
+ ('direct', XRenderDirectFormat),
34
+ ('colormap', c_ulong),
35
+ ]
36
+
37
+ # XRenderFindVisualFormat(Display *dpy, Visual *visual)
38
+ try:
39
+ XRenderFindVisualFormat = lib.XRenderFindVisualFormat
40
+ XRenderFindVisualFormat.argtypes = [c_void_p, POINTER(Visual)]
41
+ XRenderFindVisualFormat.restype = POINTER(XRenderPictFormat)
42
+ except ValueError:
43
+ XRenderFindVisualFormat = None
pyglet/libs/x11/xsync.py CHANGED
@@ -406,6 +406,49 @@ XSyncGetPriority = _lib.XSyncGetPriority
406
406
  XSyncGetPriority.restype = c_int
407
407
  XSyncGetPriority.argtypes = [POINTER(Display), XID, POINTER(c_int)]
408
408
 
409
+ # Shape kind
410
+ ShapeBounding = 0
411
+ ShapeClip = 1
412
+ ShapeInput = 2
413
+
414
+ # Shape operation
415
+ ShapeSet = 0
416
+ ShapeUnion = 1
417
+ ShapeIntersect = 2
418
+ ShapeSubtract = 3
419
+ ShapeInvert = 4
420
+
421
+ XShapeCombineRegion = _lib.XShapeCombineRegion
422
+ XShapeCombineRegion.argtypes = [
423
+ POINTER(Display),
424
+ c_void_p,
425
+ ctypes.c_int, # shape kind
426
+ ctypes.c_int, ctypes.c_int, # x, y offset
427
+ c_void_p,
428
+ ctypes.c_int # ShapeOp
429
+ ]
430
+
431
+ XShapeCombineMask = _lib.XShapeCombineMask
432
+ XShapeCombineMask.argtypes = [
433
+ Display, # *display
434
+ c_void_p, # dest window
435
+ ctypes.c_int, # shape_kind (e.g., ShapeInput)
436
+ ctypes.c_int, # x offset
437
+ ctypes.c_int, # y offset
438
+ ctypes.c_ulong, # Pixmap (can be 0 for None)
439
+ ctypes.c_int # ShapeOp (e.g., ShapeSet)
440
+ ]
441
+ XShapeCombineMask.restype = None
442
+
443
+ XShapeQueryExtension = _lib.XShapeQueryExtension
444
+ XShapeQueryExtension.argtypes = [Display, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
445
+ XShapeQueryExtension.restype = Bool
446
+
447
+ def has_shape_support(display: Display) -> bool:
448
+ event_base = ctypes.c_int()
449
+ error_base = ctypes.c_int()
450
+ return XShapeQueryExtension(display, ctypes.byref(event_base), ctypes.byref(error_base))
451
+
409
452
 
410
453
  __all__ = ['SYNC_MAJOR_VERSION', 'SYNC_MINOR_VERSION', 'X_SyncInitialize',
411
454
  'X_SyncListSystemCounters', 'X_SyncCreateCounter', 'X_SyncSetCounter',
pyglet/math.py CHANGED
@@ -191,7 +191,7 @@ class Vec2(_typing.NamedTuple):
191
191
  )
192
192
 
193
193
  def __lt__(self, other: tuple[float, float]) -> bool:
194
- return self[0] ** 2 + self[0] ** 2 < other[0] ** 2 + other[1] ** 2
194
+ return self[0] ** 2 + self[1] ** 2 < other[0] ** 2 + other[1] ** 2
195
195
 
196
196
  @staticmethod
197
197
  def from_heading(heading: float, length: float = 1.0) -> Vec2:
@@ -301,8 +301,7 @@ class Vec2(_typing.NamedTuple):
301
301
  This simply means the vector will have a length of 1.0. If the vector
302
302
  has a length of 0, the original vector will be returned.
303
303
  """
304
- d = _math.sqrt(self[0] ** 2 + self[1] ** 2)
305
- if d:
304
+ if d := _math.sqrt(self[0] ** 2 + self[1] ** 2):
306
305
  return Vec2(self[0] / d, self[1] / d)
307
306
  return self
308
307
 
@@ -823,7 +822,8 @@ class Vec4(_typing.NamedTuple):
823
822
  )
824
823
 
825
824
  def __lt__(self, other: Vec4 | tuple[float, float, float, float]) -> bool:
826
- return self[0] ** 2 + self[1] ** 2 + self[2] ** 2 < other[0] ** 2 + other[1] ** 2 + other[2] ** 2
825
+ return (self[0] ** 2 + self[1] ** 2 + self[2] ** 2 + self[3] ** 2 <
826
+ other[0] ** 2 + other[1] ** 2 + other[2] ** 2 + other[3] ** 2)
827
827
 
828
828
  def length(self) -> float:
829
829
  """Calculate the length of the vector: ``sqrt(x ** 2 + y ** 2 + z ** 2 + w ** 2)``."""
@@ -872,8 +872,7 @@ class Vec4(_typing.NamedTuple):
872
872
  This means that the vector will have the same direction, but a length of 1.0.
873
873
  If the vector has a length of 0, the original vector will be returned.
874
874
  """
875
- d = _math.sqrt(self[0] ** 2 + self[1] ** 2 + self[2] ** 2 + self[3] ** 2)
876
- if d:
875
+ if d := _math.sqrt(self[0] ** 2 + self[1] ** 2 + self[2] ** 2 + self[3] ** 2):
877
876
  return Vec4(self[0] / d, self[1] / d, self[2] / d, self[3] / d)
878
877
  return self
879
878
 
@@ -1053,7 +1052,21 @@ class Mat3(_typing.NamedTuple):
1053
1052
  def __matmul__(self, other: Mat3) -> Mat3: ...
1054
1053
 
1055
1054
  def __matmul__(self, other) -> Vec3 | Mat3:
1056
- if isinstance(other, Vec3):
1055
+ try:
1056
+ # extract the elements in row-column form. (matrix is stored column first)
1057
+ a11, a12, a13, a21, a22, a23, a31, a32, a33 = self
1058
+ b11, b12, b13, b21, b22, b23, b31, b32, b33 = other
1059
+
1060
+ # Multiply and sum rows * columns
1061
+ return Mat3(
1062
+ # Column 1
1063
+ a11 * b11 + a21 * b12 + a31 * b13, a12 * b11 + a22 * b12 + a32 * b13, a13 * b11 + a23 * b12 + a33 * b13,
1064
+ # Column 2
1065
+ a11 * b21 + a21 * b22 + a31 * b23, a12 * b21 + a22 * b22 + a32 * b23, a13 * b21 + a23 * b22 + a33 * b23,
1066
+ # Column 3
1067
+ a11 * b31 + a21 * b32 + a31 * b33, a12 * b31 + a22 * b32 + a32 * b33, a13 * b31 + a23 * b32 + a33 * b33,
1068
+ )
1069
+ except ValueError:
1057
1070
  x, y, z = other
1058
1071
  # extract the elements in row-column form. (matrix is stored column first)
1059
1072
  a11, a12, a13, a21, a22, a23, a31, a32, a33 = self
@@ -1063,24 +1076,6 @@ class Mat3(_typing.NamedTuple):
1063
1076
  a13 * x + a23 * y + a33 * z,
1064
1077
  )
1065
1078
 
1066
- if not isinstance(other, Mat3):
1067
- msg = "Can only multiply with Mat3 or Vec3 types"
1068
- raise TypeError(msg)
1069
-
1070
- # extract the elements in row-column form. (matrix is stored column first)
1071
- a11, a12, a13, a21, a22, a23, a31, a32, a33 = self
1072
- b11, b12, b13, b21, b22, b23, b31, b32, b33 = other
1073
-
1074
- # Multiply and sum rows * columns
1075
- return Mat3(
1076
- # Column 1
1077
- a11 * b11 + a21 * b12 + a31 * b13, a12 * b11 + a22 * b12 + a32 * b13, a13 * b11 + a23 * b12 + a33 * b13,
1078
- # Column 2
1079
- a11 * b21 + a21 * b22 + a31 * b23, a12 * b21 + a22 * b22 + a32 * b23, a13 * b21 + a23 * b22 + a33 * b23,
1080
- # Column 3
1081
- a11 * b31 + a21 * b32 + a31 * b33, a12 * b31 + a22 * b32 + a32 * b33, a13 * b31 + a23 * b32 + a33 * b33,
1082
- )
1083
-
1084
1079
  def __repr__(self) -> str:
1085
1080
  return f"{self.__class__.__name__}{self[0:3]}\n {self[3:6]}\n {self[6:9]}"
1086
1081
 
@@ -1357,7 +1352,26 @@ class Mat4(_typing.NamedTuple):
1357
1352
  def __matmul__(self, other: Mat4) -> Mat4: ...
1358
1353
 
1359
1354
  def __matmul__(self, other):
1360
- if isinstance(other, Vec4):
1355
+ try:
1356
+ # extract the elements in row-column form. (matrix is stored column first)
1357
+ a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44 = self
1358
+ b11, b12, b13, b14, b21, b22, b23, b24, b31, b32, b33, b34, b41, b42, b43, b44 = other
1359
+ # Multiply and sum rows * columns:
1360
+ return Mat4(
1361
+ # Column 1
1362
+ a11 * b11 + a21 * b12 + a31 * b13 + a41 * b14, a12 * b11 + a22 * b12 + a32 * b13 + a42 * b14,
1363
+ a13 * b11 + a23 * b12 + a33 * b13 + a43 * b14, a14 * b11 + a24 * b12 + a34 * b13 + a44 * b14,
1364
+ # Column 2
1365
+ a11 * b21 + a21 * b22 + a31 * b23 + a41 * b24, a12 * b21 + a22 * b22 + a32 * b23 + a42 * b24,
1366
+ a13 * b21 + a23 * b22 + a33 * b23 + a43 * b24, a14 * b21 + a24 * b22 + a34 * b23 + a44 * b24,
1367
+ # Column 3
1368
+ a11 * b31 + a21 * b32 + a31 * b33 + a41 * b34, a12 * b31 + a22 * b32 + a32 * b33 + a42 * b34,
1369
+ a13 * b31 + a23 * b32 + a33 * b33 + a43 * b34, a14 * b31 + a24 * b32 + a34 * b33 + a44 * b34,
1370
+ # Column 4
1371
+ a11 * b41 + a21 * b42 + a31 * b43 + a41 * b44, a12 * b41 + a22 * b42 + a32 * b43 + a42 * b44,
1372
+ a13 * b41 + a23 * b42 + a33 * b43 + a43 * b44, a14 * b41 + a24 * b42 + a34 * b43 + a44 * b44,
1373
+ )
1374
+ except ValueError:
1361
1375
  x, y, z, w = other
1362
1376
  # extract the elements in row-column form. (matrix is stored column first)
1363
1377
  a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44 = self
@@ -1368,29 +1382,6 @@ class Mat4(_typing.NamedTuple):
1368
1382
  x * a14 + y * a24 + z * a34 + w * a44,
1369
1383
  )
1370
1384
 
1371
- if not isinstance(other, Mat4):
1372
- msg = "Can only multiply with Mat4 or Vec4 types"
1373
- raise TypeError(msg)
1374
-
1375
- # extract the elements in row-column form. (matrix is stored column first)
1376
- a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43, a44 = self
1377
- b11, b12, b13, b14, b21, b22, b23, b24, b31, b32, b33, b34, b41, b42, b43, b44 = other
1378
- # Multiply and sum rows * columns:
1379
- return Mat4(
1380
- # Column 1
1381
- a11 * b11 + a21 * b12 + a31 * b13 + a41 * b14, a12 * b11 + a22 * b12 + a32 * b13 + a42 * b14,
1382
- a13 * b11 + a23 * b12 + a33 * b13 + a43 * b14, a14 * b11 + a24 * b12 + a34 * b13 + a44 * b14,
1383
- # Column 2
1384
- a11 * b21 + a21 * b22 + a31 * b23 + a41 * b24, a12 * b21 + a22 * b22 + a32 * b23 + a42 * b24,
1385
- a13 * b21 + a23 * b22 + a33 * b23 + a43 * b24, a14 * b21 + a24 * b22 + a34 * b23 + a44 * b24,
1386
- # Column 3
1387
- a11 * b31 + a21 * b32 + a31 * b33 + a41 * b34, a12 * b31 + a22 * b32 + a32 * b33 + a42 * b34,
1388
- a13 * b31 + a23 * b32 + a33 * b33 + a43 * b34, a14 * b31 + a24 * b32 + a34 * b33 + a44 * b34,
1389
- # Column 4
1390
- a11 * b41 + a21 * b42 + a31 * b43 + a41 * b44, a12 * b41 + a22 * b42 + a32 * b43 + a42 * b44,
1391
- a13 * b41 + a23 * b42 + a33 * b43 + a43 * b44, a14 * b41 + a24 * b42 + a34 * b43 + a44 * b44,
1392
- )
1393
-
1394
1385
  def __repr__(self) -> str:
1395
1386
  return f"{self.__class__.__name__}{self[0:4]}\n {self[4:8]}\n {self[8:12]}\n {self[12:16]}"
1396
1387
 
@@ -1,5 +1,5 @@
1
1
  """
2
- Responsabilities
2
+ Responsibilities
3
3
 
4
4
  Handles accumulation of debug events while playing media_player and saves
5
5
  when sample's play ends.
@@ -126,7 +126,7 @@ class FFmpegException(MediaFormatException):
126
126
  def ffmpeg_get_audio_buffer_size(audio_format):
127
127
  """Return the audio buffer size
128
128
 
129
- Buffer size can accomodate 1 sec of audio data.
129
+ Buffer size can accommodate 1 sec of audio data.
130
130
  """
131
131
  return audio_format.bytes_per_second + FF_INPUT_BUFFER_PADDING_SIZE
132
132
 
@@ -385,16 +385,6 @@ def ffmpeg_stream_info(file: FFmpegFile, stream_index: int) -> StreamAudioInfo |
385
385
  context.sample_rate,
386
386
  channel_count,
387
387
  )
388
- if context.format in (AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8P):
389
- info.sample_bits = 8
390
- elif context.format in (AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P,
391
- AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP):
392
- info.sample_bits = 16
393
- elif context.format in (AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P):
394
- info.sample_bits = 32
395
- else:
396
- info.sample_format = None
397
- info.sample_bits = None
398
388
  else:
399
389
  return None
400
390
  return info
@@ -466,7 +456,7 @@ def ffmpeg_seek_file(file: FFmpegFile, timestamp: float) -> None:
466
456
  buf = create_string_buffer(128)
467
457
  avutil.av_strerror(result, buf, 128)
468
458
  descr = buf.value
469
- raise FFmpegException('Error occured while seeking. ' +
459
+ raise FFmpegException('Error occurred while seeking. ' +
470
460
  descr.decode())
471
461
 
472
462
 
@@ -634,35 +624,29 @@ class FFmpegSource(StreamingSource):
634
624
  self._video_stream = stream
635
625
  self._video_stream_index = i
636
626
 
637
- elif isinstance(info, StreamAudioInfo) and info.sample_bits in (8, 16, 24) and self._audio_stream is None:
627
+ elif isinstance(info, StreamAudioInfo) and self._audio_stream is None:
638
628
  stream = ffmpeg_open_stream(self._file, i)
639
629
 
640
- self.audio_format = AudioFormat(
641
- channels=min(2, info.channels),
642
- sample_size=info.sample_bits,
643
- sample_rate=info.sample_rate)
644
- self._audio_stream = stream
645
- self._audio_stream_index = i
646
-
647
- channel_input = self._get_default_channel_layout(info.channels)
648
630
  channels_out = min(2, info.channels)
631
+ channel_input = self._get_default_channel_layout(info.channels)
649
632
  channel_output = self._get_default_channel_layout(channels_out)
650
633
 
651
- sample_rate = stream.codec_context.contents.sample_rate
652
- sample_format = stream.codec_context.contents.sample_fmt
653
-
654
- if sample_format in (AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8P):
634
+ sample_bits = info.sample_bits
635
+ if info.sample_format in (AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8P):
655
636
  self.tgt_format = AV_SAMPLE_FMT_U8
656
- elif sample_format in (AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P):
657
- self.tgt_format = AV_SAMPLE_FMT_S16
658
- elif sample_format in (AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P):
659
- self.tgt_format = AV_SAMPLE_FMT_S32
660
- elif sample_format in (AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP):
661
- self.tgt_format = AV_SAMPLE_FMT_S16
662
637
  else:
663
- raise FFmpegException('Audio format not supported.')
638
+ # No matter the input format, produce S16 samples.
639
+ sample_bits = 16
640
+ self.tgt_format = AV_SAMPLE_FMT_S16
641
+
642
+ self.audio_format = AudioFormat(
643
+ channels=channels_out,
644
+ sample_size=sample_bits,
645
+ sample_rate=info.sample_rate)
646
+ self._audio_stream = stream
647
+ self._audio_stream_index = i
664
648
 
665
- self.audio_convert_ctx = self.get_formatted_swr_context(channel_output, sample_rate, channel_input, sample_format)
649
+ self.audio_convert_ctx = self.get_formatted_swr_context(channel_output, info.sample_rate, channel_input, info.sample_format)
666
650
  if not self.audio_convert_ctx:
667
651
  swresample.swr_free(self.audio_convert_ctx)
668
652
  raise FFmpegException('Cannot create sample rate converter.')
@@ -676,7 +660,7 @@ class FFmpegSource(StreamingSource):
676
660
  self._events = [] # They don't seem to be used!
677
661
 
678
662
  self.audioq = deque()
679
- # Make queue big enough to accomodate 1.2 sec?
663
+ # Make queue big enough to accommodate 1.2 sec?
680
664
  self._max_len_audioq = self.MAX_QUEUE_SIZE # Need to figure out a correct amount
681
665
  if self.audio_format:
682
666
  # Buffer 1 sec worth of audio
@@ -48,7 +48,7 @@ class _MessageHandler:
48
48
  """The main message callback"""
49
49
  if message.type == Gst.MessageType.EOS:
50
50
 
51
- self.source.queue.put(self.source.sentinal)
51
+ self.source.queue.put(self.source.sentinel)
52
52
  if not self.source.caps:
53
53
  raise GStreamerDecodeException("Appears to be an unsupported file")
54
54
 
@@ -105,7 +105,7 @@ class _MessageHandler:
105
105
  class GStreamerSource(StreamingSource):
106
106
 
107
107
  source_instances = weakref.WeakSet()
108
- sentinal = object()
108
+ sentinel = object()
109
109
 
110
110
  def __init__(self, filename, file=None):
111
111
  self._pipeline = Gst.Pipeline()
@@ -199,7 +199,7 @@ class GStreamerSource(StreamingSource):
199
199
  data = bytes()
200
200
  while len(data) < num_bytes:
201
201
  packet = self.queue.get()
202
- if packet == self.sentinal:
202
+ if packet == self.sentinel:
203
203
  self._finished.set()
204
204
  break
205
205
  data += packet
@@ -287,7 +287,7 @@ class MemoryFLACFileStream(UnclosedFLACFileStream):
287
287
 
288
288
  metadata_status = pyogg.flac.FLAC__stream_decoder_process_until_end_of_metadata(self.decoder)
289
289
  if not metadata_status: # error
290
- raise DecodeException("An error occured when trying to decode the metadata of {}".format(path))
290
+ raise DecodeException("An error occurred when trying to decode the metadata of {}".format(path))
291
291
 
292
292
  def read_callback(self, decoder, buffer, size, data):
293
293
  chunk = size.contents.value
@@ -25,6 +25,12 @@ class WaveSource(StreamingSource):
25
25
 
26
26
  nchannels, sampwidth, framerate, nframes, comptype, compname = self._wave.getparams()
27
27
 
28
+ if nchannels not in (1, 2):
29
+ raise WAVEDecodeException(f"incompatible channel count {nchannels}")
30
+
31
+ if sampwidth not in (1, 2):
32
+ raise WAVEDecodeException(f"incompatible sample width {sampwidth}")
33
+
28
34
  self.audio_format = AudioFormat(channels=nchannels, sample_size=sampwidth * 8, sample_rate=framerate)
29
35
 
30
36
  self._bytes_per_frame = nchannels * sampwidth
@@ -539,19 +539,45 @@ class WMFSource(Source):
539
539
  self._source_reader.SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, True)
540
540
 
541
541
  # Check sub media type, AKA what kind of codec
542
- guid_compressed = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
543
- imfmedia.GetGUID(MF_MT_SUBTYPE, byref(guid_compressed))
542
+ source_subtype_guid = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
543
+ source_sample_size = c_uint32()
544
+ source_channel_count = c_uint32()
544
545
 
545
- if guid_compressed == MFAudioFormat_PCM or guid_compressed == MFAudioFormat_Float:
546
- assert _debug(f'WMFAudioDecoder: Found Uncompressed Audio: {guid_compressed}')
546
+ imfmedia.GetGUID(MF_MT_SUBTYPE, byref(source_subtype_guid))
547
+ try:
548
+ # Some formats such as mp3 do not report this value
549
+ imfmedia.GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, byref(source_sample_size))
550
+ except OSError:
551
+ source_sample_size.value = 0
552
+ imfmedia.GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, byref(source_channel_count))
553
+
554
+ if (
555
+ source_subtype_guid == MFAudioFormat_PCM and
556
+ source_sample_size.value in (8, 16) and
557
+ source_channel_count.value in (1, 2)
558
+ ):
559
+ assert _debug(f'WMFAudioDecoder: Found compatible Integer PCM Audio: {source_subtype_guid}')
547
560
  else:
548
- assert _debug(f'WMFAudioDecoder: Found Compressed Audio: {guid_compressed}')
549
- # If audio is compressed, attempt to decompress it by forcing source reader to use PCM
550
- mf_mediatype = IMFMediaType()
561
+ assert _debug(f'WMFAudioDecoder: Found incompatible Audio: {source_subtype_guid}, '
562
+ f'sample size={source_sample_size.value}, channel count={source_channel_count.value}.'
563
+ f'Attempting to decode/resample.')
564
+ # If audio is compressed or incompatible, attempt to decompress or resample it
565
+ # to standard 16bit integer PCM
566
+ samples_per_sec = c_uint32()
567
+ imfmedia.GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, byref(samples_per_sec))
551
568
 
569
+ channels_out = min(2, source_channel_count.value)
570
+
571
+ mf_mediatype = IMFMediaType()
552
572
  MFCreateMediaType(byref(mf_mediatype))
553
573
  mf_mediatype.SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio)
554
574
  mf_mediatype.SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM)
575
+ mf_mediatype.SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels_out)
576
+ mf_mediatype.SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, 16)
577
+ mf_mediatype.SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, samples_per_sec.value)
578
+ mf_mediatype.SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, channels_out * 2)
579
+ mf_mediatype.SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, samples_per_sec.value * channels_out * 2)
580
+ mf_mediatype.SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1)
555
581
 
556
582
  try:
557
583
  self._source_reader.SetCurrentMediaType(self._audio_stream_index, None, mf_mediatype)
@@ -318,7 +318,7 @@ class Win32AudioDeviceManager(base.AbstractAudioDeviceManager):
318
318
  return dev_id.value, name, description, state.value
319
319
 
320
320
  def get_devices(self, flow=eRender, state=DEVICE_STATE_ACTIVE):
321
- """Get's all of the specified devices (by default, all output and active)."""
321
+ """Gets all of the specified devices (by default, all output and active)."""
322
322
  collection = IMMDeviceCollection()
323
323
  self._device_enum.EnumAudioEndpoints(flow, state, byref(collection))
324
324
 
@@ -378,7 +378,7 @@ class AbstractAudioPlayer(metaclass=ABCMeta):
378
378
  # Player falling behind
379
379
  # Skip at most 12ms if this is a minor desync, otherwise skip the entire
380
380
  # difference. this will be noticeable, but the desync is
381
- # likely already noticable in context of whatever the application does.
381
+ # likely already noticeable in context of whatever the application does.
382
382
  compensated_bytes = (-desync_bytes
383
383
  if extreme_desync
384
384
  else min(-desync_bytes, self.desync_correction_bytes_minor))
@@ -5,6 +5,7 @@ import ctypes
5
5
  import weakref
6
6
  from collections import namedtuple
7
7
 
8
+ from pyglet.media.exceptions import MediaException
8
9
  from pyglet.util import debug_print
9
10
  from pyglet.window.win32 import _user32
10
11
 
@@ -20,6 +21,9 @@ def _check(hresult):
20
21
 
21
22
 
22
23
  def _create_wave_format(audio_format):
24
+ if audio_format.channels > 2 or audio_format.sample_size not in (8, 16):
25
+ raise MediaException(f'Unsupported audio format: {audio_format}')
26
+
23
27
  wfx = lib.WAVEFORMATEX()
24
28
  wfx.wFormatTag = lib.WAVE_FORMAT_PCM
25
29
  wfx.nChannels = audio_format.channels
@@ -53,7 +53,7 @@ class AbstractListener(metaclass=ABCMeta):
53
53
  listener is facing.
54
54
 
55
55
  The orientation is given as a tuple of floats (x, y, z), and has
56
- no unit. The forward orientation should be orthagonal to the
56
+ no unit. The forward orientation should be orthogonal to the
57
57
  up orientation.
58
58
 
59
59
  :type: 3-tuple of float
@@ -69,7 +69,7 @@ class AbstractListener(metaclass=ABCMeta):
69
69
  of the listener.
70
70
 
71
71
  The orientation is given as a tuple of floats (x, y, z), and has
72
- no unit. The up orientation should be orthagonal to the
72
+ no unit. The up orientation should be orthogonal to the
73
73
  forward orientation.
74
74
 
75
75
  :type: 3-tuple of float