pyglet 3.0.dev3__py3-none-any.whl → 3.0.dev5__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.
- pyglet/__init__.py +2 -2
- pyglet/app/cocoa.py +2 -1
- pyglet/customtypes.py +13 -7
- pyglet/event.py +10 -2
- pyglet/experimental/README.md +26 -1
- pyglet/experimental/flaccodec.py +380 -0
- pyglet/experimental/geoshader_sprite.py +11 -8
- pyglet/experimental/net.py +6 -6
- pyglet/experimental/particles.py +38 -0
- pyglet/experimental/win32priority.py +68 -0
- pyglet/font/dwrite/__init__.py +1 -1
- pyglet/font/dwrite/d2d1_lib.py +1 -1
- pyglet/font/fontconfig.py +1 -1
- pyglet/graphics/__init__.py +47 -7
- pyglet/graphics/api/__init__.py +14 -25
- pyglet/graphics/api/base.py +169 -54
- pyglet/graphics/api/gl/buffer.py +144 -29
- pyglet/graphics/api/gl/cocoa/context.py +1 -1
- pyglet/graphics/api/gl/context.py +46 -7
- pyglet/graphics/api/gl/draw.py +17 -599
- pyglet/graphics/api/gl/egl/context.py +1 -1
- pyglet/graphics/api/gl/gl_info.py +4 -0
- pyglet/graphics/api/gl/global_opengl.py +1 -88
- pyglet/graphics/api/gl/renderer.py +23 -0
- pyglet/graphics/api/gl/shader.py +145 -159
- pyglet/graphics/api/gl/sprite.py +99 -0
- pyglet/graphics/api/gl/state.py +71 -50
- pyglet/graphics/api/gl/texture.py +28 -11
- pyglet/graphics/api/gl/vertexdomain.py +115 -4
- pyglet/graphics/api/gl/win32/context.py +1 -1
- pyglet/graphics/api/gl/xlib/context.py +1 -1
- pyglet/graphics/api/gl2/buffer.py +3 -4
- pyglet/graphics/api/gl2/draw.py +15 -573
- pyglet/graphics/api/gl2/global_opengl.py +1 -64
- pyglet/graphics/api/gl2/shader.py +64 -9
- pyglet/graphics/api/gl2/shapes.py +1 -1
- pyglet/graphics/api/gl2/sprite.py +87 -0
- pyglet/graphics/api/gl2/state.py +29 -0
- pyglet/graphics/api/gl2/vertexdomain.py +3 -2
- pyglet/graphics/api/webgl/__init__.py +1 -86
- pyglet/graphics/api/webgl/buffer.py +130 -31
- pyglet/graphics/api/webgl/context.py +5 -2
- pyglet/graphics/api/webgl/draw.py +10 -562
- pyglet/graphics/api/webgl/gl_info.py +5 -0
- pyglet/graphics/api/webgl/renderer.py +23 -0
- pyglet/graphics/api/webgl/shader.py +144 -154
- pyglet/graphics/api/webgl/sprite.py +99 -0
- pyglet/graphics/api/webgl/state.py +57 -59
- pyglet/graphics/api/webgl/texture.py +2 -0
- pyglet/graphics/api/webgl/vertexdomain.py +2 -1
- pyglet/graphics/atlas.py +4 -4
- pyglet/graphics/buffer.py +324 -10
- pyglet/graphics/draw.py +679 -123
- pyglet/graphics/instance.py +155 -2
- pyglet/graphics/shader.py +309 -15
- pyglet/graphics/state.py +101 -9
- pyglet/graphics/texture.py +68 -56
- pyglet/graphics/vertexdomain.py +179 -10
- pyglet/image/__init__.py +12 -12
- pyglet/image/animation.py +9 -4
- pyglet/image/base.py +8 -9
- pyglet/image/codecs/wic.py +1 -1
- pyglet/info.py +319 -125
- pyglet/input/win32/directinput.py +10 -3
- pyglet/lib.py +6 -1
- pyglet/libs/darwin/cocoapy/cocoalibs.py +83 -0
- pyglet/libs/darwin/cocoapy/runtime.py +38 -17
- pyglet/math.py +76 -42
- pyglet/media/codecs/base.py +2 -2
- pyglet/media/codecs/wave.py +17 -11
- pyglet/media/drivers/base.py +15 -8
- pyglet/media/drivers/pulse/adaptation.py +2 -2
- pyglet/media/drivers/pulse/interface.py +2 -2
- pyglet/media/player.py +9 -1
- pyglet/media/synthesis.py +3 -3
- pyglet/model/__init__.py +313 -54
- pyglet/model/codecs/__init__.py +2 -2
- pyglet/model/codecs/obj.py +7 -1
- pyglet/resource.py +74 -6
- pyglet/shapes.py +22 -35
- pyglet/sprite.py +285 -13
- pyglet/text/__init__.py +2 -2
- pyglet/text/caret.py +7 -6
- pyglet/window/__init__.py +58 -38
- pyglet/window/camera/__init__.py +28 -0
- pyglet/window/camera/base.py +682 -0
- pyglet/window/camera/camera2d.py +333 -0
- pyglet/window/camera/camera3d.py +318 -0
- pyglet/window/cocoa/__init__.py +0 -9
- pyglet/window/emscripten/__init__.py +0 -8
- pyglet/window/headless/__init__.py +0 -8
- pyglet/window/wayland/__init__.py +0 -7
- pyglet/window/win32/__init__.py +2 -5
- pyglet/window/xlib/__init__.py +2 -10
- {pyglet-3.0.dev3.dist-info → pyglet-3.0.dev5.dist-info}/METADATA +2 -2
- {pyglet-3.0.dev3.dist-info → pyglet-3.0.dev5.dist-info}/RECORD +99 -91
- pyglet/experimental/multitexture_sprite.py +0 -541
- /pyglet/{image/codecs/wincodec_lib.py → libs/win32/wincodec.py} +0 -0
- {pyglet-3.0.dev3.dist-info → pyglet-3.0.dev5.dist-info}/WHEEL +0 -0
- {pyglet-3.0.dev3.dist-info → pyglet-3.0.dev5.dist-info}/licenses/LICENSE +0 -0
pyglet/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from typing import Any, Callable, ItemsView, Sized
|
|
19
19
|
|
|
20
20
|
#: The release version
|
|
21
|
-
version = '3.0.
|
|
21
|
+
version = '3.0.dev5'
|
|
22
22
|
__version__ = version
|
|
23
23
|
|
|
24
24
|
MIN_PYTHON_VERSION = 3, 8
|
|
@@ -103,7 +103,7 @@ class Options:
|
|
|
103
103
|
|
|
104
104
|
debug_graphics_batch: bool = False
|
|
105
105
|
"""If ``True``, prints batch information being drawn, including :py:class:`~pyglet.graphics.Group`'s, VertexDomains,
|
|
106
|
-
and :py:class:`~pyglet.graphics.Texture` information. This can be useful to see how many Group's are being
|
|
106
|
+
and :py:class:`~pyglet.graphics.texture.Texture` information. This can be useful to see how many Group's are being
|
|
107
107
|
consolidated."""
|
|
108
108
|
|
|
109
109
|
debug_lib: bool = False
|
pyglet/app/cocoa.py
CHANGED
|
@@ -6,7 +6,7 @@ import time
|
|
|
6
6
|
from pyglet import app
|
|
7
7
|
from pyglet.app.base import PlatformEventLoop, EventLoop
|
|
8
8
|
from pyglet.libs.darwin import cocoapy, AutoReleasePool, ObjCSubclass, PyObjectEncoding, ObjCInstance, send_super, \
|
|
9
|
-
ObjCClass, get_selector
|
|
9
|
+
ObjCClass, get_selector, set_realtime_thread_policy
|
|
10
10
|
|
|
11
11
|
NSApplication = cocoapy.ObjCClass('NSApplication')
|
|
12
12
|
NSMenu = cocoapy.ObjCClass('NSMenu')
|
|
@@ -161,6 +161,7 @@ class CocoaPlatformEventLoop(PlatformEventLoop):
|
|
|
161
161
|
|
|
162
162
|
def __init__(self):
|
|
163
163
|
super().__init__()
|
|
164
|
+
set_realtime_thread_policy()
|
|
164
165
|
self._timer = None
|
|
165
166
|
|
|
166
167
|
with AutoReleasePool():
|
pyglet/customtypes.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import ctypes
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
|
-
from typing import Union, Literal, Type, Protocol, Tuple
|
|
6
|
+
from typing import Union, Literal, Type, Protocol, Tuple, runtime_checkable
|
|
7
7
|
|
|
8
8
|
from ctypes import _SimpleCData, _Pointer # type: ignore # noqa: PGH003
|
|
9
9
|
|
|
@@ -22,8 +22,8 @@ MediaTypes = Literal["audio", "video", "image"]
|
|
|
22
22
|
|
|
23
23
|
Number = Union[int, float]
|
|
24
24
|
|
|
25
|
-
RGBColor = Tuple[
|
|
26
|
-
RGBAColor = Tuple[
|
|
25
|
+
RGBColor = Tuple[int, int, int]
|
|
26
|
+
RGBAColor = Tuple[int, int, int, int]
|
|
27
27
|
|
|
28
28
|
DataTypes = Literal[
|
|
29
29
|
'f', # float
|
|
@@ -44,11 +44,16 @@ CTypesPointer = _Pointer
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
@runtime_checkable
|
|
47
48
|
class ScissorProtocol(Protocol):
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
"""Protocol for an object that holds information for scissor states."""
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def area(self) -> tuple[int, int, int, int]:
|
|
53
|
+
"""Return the region of the scissor area.
|
|
54
|
+
|
|
55
|
+
Requires the x, y, width, and height in screen space coordinates.
|
|
56
|
+
"""
|
|
52
57
|
|
|
53
58
|
|
|
54
59
|
|
|
@@ -64,4 +69,5 @@ __all__ = [
|
|
|
64
69
|
"MediaTypes",
|
|
65
70
|
"RGBAColor",
|
|
66
71
|
"RGBColor",
|
|
72
|
+
"ScissorProtocol",
|
|
67
73
|
]
|
pyglet/event.py
CHANGED
|
@@ -356,8 +356,16 @@ class EventDispatcher:
|
|
|
356
356
|
if not handler:
|
|
357
357
|
continue
|
|
358
358
|
if isinstance(handler, WeakMethod):
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
weak_handler = handler
|
|
360
|
+
handler = weak_handler()
|
|
361
|
+
if handler is None:
|
|
362
|
+
# Weak handlers can expire before their callback is removed from stack.
|
|
363
|
+
# Skip and remove stale handlers
|
|
364
|
+
if frame.get(event_type) is weak_handler:
|
|
365
|
+
del frame[event_type]
|
|
366
|
+
if not frame and frame in self._event_stack:
|
|
367
|
+
self._event_stack.remove(frame)
|
|
368
|
+
continue
|
|
361
369
|
try:
|
|
362
370
|
invoked = True
|
|
363
371
|
if handler(*args):
|
pyglet/experimental/README.md
CHANGED
|
@@ -3,4 +3,29 @@ Experimental modules
|
|
|
3
3
|
|
|
4
4
|
This package contains experimental modules, which are included here for
|
|
5
5
|
wider testing and feedback. Anything contained within may be broken, refactored,
|
|
6
|
-
or removed without notice.
|
|
6
|
+
or removed without notice.
|
|
7
|
+
|
|
8
|
+
A module may belong in `experimental` when it meets one or more of the following criteria:
|
|
9
|
+
|
|
10
|
+
- The API is still being designed.
|
|
11
|
+
- The implementation needs wider testing.
|
|
12
|
+
- The feature depends on external tools, generated assets, or optional workflows.
|
|
13
|
+
- The feature is useful, but too specialized for the core package.
|
|
14
|
+
- The feature introduces complexity that should not be required for normal users.
|
|
15
|
+
- The feature may eventually move into the stable package after review.
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
Each experimental module should include documentation explaining:
|
|
20
|
+
|
|
21
|
+
- What the module solves or does.
|
|
22
|
+
- What tools, files, or assets are required.
|
|
23
|
+
- A minimal example, either as a `"__main__"` execution, or docstring.
|
|
24
|
+
|
|
25
|
+
## Graduation to stable API
|
|
26
|
+
|
|
27
|
+
An experimental module may be considered for promotion to the stable package when:
|
|
28
|
+
|
|
29
|
+
- The API has proven useful and reasonably stable.
|
|
30
|
+
- The feature has documentation and examples.
|
|
31
|
+
- Maintainers are willing to support it long-term.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import struct
|
|
3
|
+
|
|
4
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
5
|
+
|
|
6
|
+
from pyglet.util import DecodeException
|
|
7
|
+
from pyglet.media.codecs.base import StreamingSource, AudioData, AudioFormat, StaticSource
|
|
8
|
+
from pyglet.media.codecs import MediaDecoder
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# This module uses code from Project Nayuki:
|
|
12
|
+
# https://www.nayuki.io/page/simple-flac-implementation
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FLACDecodeException(DecodeException):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
FIXED_PREDICTION_COEFFICIENTS = ((), (1,), (2, -1), (3, -3, 1), (4, -6, 4, -1))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _initializer(filename, numchannels, sample_size):
|
|
23
|
+
"""Function to open a copy of the file locally in each Process."""
|
|
24
|
+
global _file
|
|
25
|
+
global _numchannels
|
|
26
|
+
global _sample_size
|
|
27
|
+
_file = open(filename, 'rb')
|
|
28
|
+
_numchannels = numchannels
|
|
29
|
+
_sample_size = sample_size
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def decode_frame(offset):
|
|
33
|
+
try:
|
|
34
|
+
# Read a ton of header fields, and ignore most of them
|
|
35
|
+
numchannels = _numchannels
|
|
36
|
+
sample_size = _sample_size
|
|
37
|
+
_file.seek(offset)
|
|
38
|
+
inp = BitInputStream(_file)
|
|
39
|
+
|
|
40
|
+
temp = inp.read_byte()
|
|
41
|
+
if temp == -1:
|
|
42
|
+
return False
|
|
43
|
+
sync = temp << 6 | inp.read_uint(6)
|
|
44
|
+
if sync != 0x3FFE:
|
|
45
|
+
raise FLACDecodeException("Sync code expected")
|
|
46
|
+
|
|
47
|
+
inp.read_uint(1)
|
|
48
|
+
inp.read_uint(1)
|
|
49
|
+
blocksizecode = inp.read_uint(4)
|
|
50
|
+
sampleratecode = inp.read_uint(4)
|
|
51
|
+
chanasgn = inp.read_uint(4)
|
|
52
|
+
inp.read_uint(3)
|
|
53
|
+
inp.read_uint(1)
|
|
54
|
+
|
|
55
|
+
temp = inp.read_uint(8)
|
|
56
|
+
while temp >= 0b11000000:
|
|
57
|
+
inp.read_uint(8)
|
|
58
|
+
temp = (temp << 1) & 0xFF
|
|
59
|
+
|
|
60
|
+
if blocksizecode == 1:
|
|
61
|
+
blocksize = 192
|
|
62
|
+
elif 2 <= blocksizecode <= 5:
|
|
63
|
+
blocksize = 576 << blocksizecode - 2
|
|
64
|
+
elif blocksizecode == 6:
|
|
65
|
+
blocksize = inp.read_uint(8) + 1
|
|
66
|
+
elif blocksizecode == 7:
|
|
67
|
+
blocksize = inp.read_uint(16) + 1
|
|
68
|
+
elif 8 <= blocksizecode <= 15:
|
|
69
|
+
blocksize = 256 << (blocksizecode - 8)
|
|
70
|
+
else:
|
|
71
|
+
raise FLACDecodeException("Invalid Blocksize")
|
|
72
|
+
|
|
73
|
+
if sampleratecode == 12:
|
|
74
|
+
inp.read_uint(8)
|
|
75
|
+
elif sampleratecode in (13, 14):
|
|
76
|
+
inp.read_uint(16)
|
|
77
|
+
|
|
78
|
+
crc8 = inp.read_byte()
|
|
79
|
+
|
|
80
|
+
# Decode each channel's subframe, then skip footer
|
|
81
|
+
if 0 <= chanasgn <= 7:
|
|
82
|
+
samples = [decode_subframe(inp, blocksize, sample_size) for _ in range(chanasgn + 1)]
|
|
83
|
+
elif 8 <= chanasgn <= 10:
|
|
84
|
+
temp0 = decode_subframe(inp, blocksize, sample_size + (1 if (chanasgn == 9) else 0))
|
|
85
|
+
temp1 = decode_subframe(inp, blocksize, sample_size + (0 if (chanasgn == 9) else 1))
|
|
86
|
+
if chanasgn == 8:
|
|
87
|
+
for i in range(blocksize):
|
|
88
|
+
temp1[i] = temp0[i] - temp1[i]
|
|
89
|
+
elif chanasgn == 9:
|
|
90
|
+
for i in range(blocksize):
|
|
91
|
+
temp0[i] += temp1[i]
|
|
92
|
+
elif chanasgn == 10:
|
|
93
|
+
for i in range(blocksize):
|
|
94
|
+
side = temp1[i]
|
|
95
|
+
right = temp0[i] - (side >> 1)
|
|
96
|
+
temp1[i] = right
|
|
97
|
+
temp0[i] = right + side
|
|
98
|
+
samples = [temp0, temp1]
|
|
99
|
+
else:
|
|
100
|
+
raise FLACDecodeException("Reserved channel assignment")
|
|
101
|
+
|
|
102
|
+
inp.align_to_byte()
|
|
103
|
+
inp.read_byte()
|
|
104
|
+
inp.read_byte()
|
|
105
|
+
|
|
106
|
+
# Write the decoded samples
|
|
107
|
+
fmt = str(blocksize * numchannels) + 'h'
|
|
108
|
+
if sample_size == 8:
|
|
109
|
+
return struct.pack(fmt, *(samples[i][j] + 128 for j in range(blocksize) for i in range(numchannels)))
|
|
110
|
+
else:
|
|
111
|
+
return struct.pack(fmt, *(samples[i][j] for j in range(blocksize) for i in range(numchannels)))
|
|
112
|
+
except:
|
|
113
|
+
return b""
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def decode_subframe(inp, blocksize, sampledepth):
|
|
117
|
+
inp.read_uint(1)
|
|
118
|
+
subframe_type = inp.read_uint(6)
|
|
119
|
+
shift = inp.read_uint(1)
|
|
120
|
+
if shift == 1:
|
|
121
|
+
while inp.read_uint(1) == 0:
|
|
122
|
+
shift += 1
|
|
123
|
+
sampledepth -= shift
|
|
124
|
+
|
|
125
|
+
if subframe_type == 0: # Constant coding
|
|
126
|
+
result = [inp.read_signed_int(sampledepth)] * blocksize
|
|
127
|
+
elif subframe_type == 1: # Verbatim coding
|
|
128
|
+
result = [inp.read_signed_int(sampledepth) for _ in range(blocksize)]
|
|
129
|
+
elif 8 <= subframe_type <= 12:
|
|
130
|
+
result = decode_fixed_prediction_subframe(inp, subframe_type - 8, blocksize, sampledepth)
|
|
131
|
+
elif 32 <= subframe_type <= 63:
|
|
132
|
+
result = decode_linear_predictive_coding_subframe(inp, subframe_type - 31, blocksize, sampledepth)
|
|
133
|
+
else:
|
|
134
|
+
raise FLACDecodeException("Reserved subframe type")
|
|
135
|
+
return [(v << shift) for v in result]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def decode_fixed_prediction_subframe(inp, predorder, blocksize, sampledepth):
|
|
139
|
+
result = [inp.read_signed_int(sampledepth) for _ in range(predorder)]
|
|
140
|
+
decode_residuals(inp, blocksize, result)
|
|
141
|
+
restore_linear_prediction(result, FIXED_PREDICTION_COEFFICIENTS[predorder], 0)
|
|
142
|
+
return result
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def decode_linear_predictive_coding_subframe(inp, lpcorder, blocksize, sampledepth):
|
|
146
|
+
result = [inp.read_signed_int(sampledepth) for _ in range(lpcorder)]
|
|
147
|
+
precision = inp.read_uint(4) + 1
|
|
148
|
+
shift = inp.read_signed_int(5)
|
|
149
|
+
coefs = [inp.read_signed_int(precision) for _ in range(lpcorder)]
|
|
150
|
+
decode_residuals(inp, blocksize, result)
|
|
151
|
+
restore_linear_prediction(result, coefs, shift)
|
|
152
|
+
return result
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def decode_residuals(inp, blocksize, result):
|
|
156
|
+
method = inp.read_uint(2)
|
|
157
|
+
if method >= 2:
|
|
158
|
+
raise FLACDecodeException("Reserved residual coding method")
|
|
159
|
+
parambits = [4, 5][method]
|
|
160
|
+
escapeparam = [0xF, 0x1F][method]
|
|
161
|
+
|
|
162
|
+
partitionorder = inp.read_uint(4)
|
|
163
|
+
numpartitions = 1 << partitionorder
|
|
164
|
+
if blocksize % numpartitions != 0:
|
|
165
|
+
raise FLACDecodeException("Block size not divisible by number of Rice partitions")
|
|
166
|
+
|
|
167
|
+
for i in range(numpartitions):
|
|
168
|
+
count = blocksize >> partitionorder
|
|
169
|
+
if i == 0:
|
|
170
|
+
count -= len(result)
|
|
171
|
+
param = inp.read_uint(parambits)
|
|
172
|
+
if param < escapeparam:
|
|
173
|
+
result.extend(inp.read_rice_signed_int(param) for _ in range(count))
|
|
174
|
+
else:
|
|
175
|
+
numbits = inp.read_uint(5)
|
|
176
|
+
result.extend(inp.read_signed_int(numbits) for _ in range(count))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def restore_linear_prediction(result, coefs, shift):
|
|
180
|
+
for i in range(len(coefs), len(result)):
|
|
181
|
+
result[i] += sum((result[i - 1 - j] * c) for (j, c) in enumerate(coefs)) >> shift
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class BitInputStream:
|
|
185
|
+
__slots__ = 'inp', 'bitbuffer', 'bitbufferlen'
|
|
186
|
+
|
|
187
|
+
def __init__(self, inp):
|
|
188
|
+
self.inp = inp
|
|
189
|
+
self.bitbuffer = 0
|
|
190
|
+
self.bitbufferlen = 0
|
|
191
|
+
|
|
192
|
+
def align_to_byte(self):
|
|
193
|
+
self.bitbufferlen -= self.bitbufferlen % 8
|
|
194
|
+
|
|
195
|
+
def read_byte(self):
|
|
196
|
+
if self.bitbufferlen >= 8:
|
|
197
|
+
return self.read_uint(8)
|
|
198
|
+
else:
|
|
199
|
+
result = self.inp.read(1)
|
|
200
|
+
if len(result) == 0:
|
|
201
|
+
return -1
|
|
202
|
+
return result[0]
|
|
203
|
+
|
|
204
|
+
def read_uint(self, n):
|
|
205
|
+
bitbuffer = self.bitbuffer
|
|
206
|
+
bitbufferlen = self.bitbufferlen
|
|
207
|
+
|
|
208
|
+
while bitbufferlen < n:
|
|
209
|
+
temp = self.inp.read(1)
|
|
210
|
+
if len(temp) == 0:
|
|
211
|
+
raise EOFError()
|
|
212
|
+
temp = temp[0]
|
|
213
|
+
bitbuffer = (bitbuffer << 8) | temp
|
|
214
|
+
bitbufferlen += 8
|
|
215
|
+
bitbufferlen -= n
|
|
216
|
+
result = (bitbuffer >> bitbufferlen) & ((1 << n) - 1)
|
|
217
|
+
bitbuffer &= (1 << bitbufferlen) - 1
|
|
218
|
+
|
|
219
|
+
self.bitbuffer = bitbuffer
|
|
220
|
+
self.bitbufferlen = bitbufferlen
|
|
221
|
+
return result
|
|
222
|
+
|
|
223
|
+
def read_signed_int(self, n):
|
|
224
|
+
temp = self.read_uint(n)
|
|
225
|
+
temp -= (temp >> (n - 1)) << n
|
|
226
|
+
return temp
|
|
227
|
+
|
|
228
|
+
def read_rice_signed_int(self, param):
|
|
229
|
+
read_uint = self.read_uint
|
|
230
|
+
val = 0
|
|
231
|
+
while read_uint(1) == 0:
|
|
232
|
+
val += 1
|
|
233
|
+
val = (val << param) | read_uint(param)
|
|
234
|
+
return (val >> 1) ^ -(val & 1)
|
|
235
|
+
|
|
236
|
+
def close(self):
|
|
237
|
+
self.inp.close()
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _find_frame_indicies(data):
|
|
241
|
+
magic1 = struct.pack('BB', int('11111111', 2), int('11111000', 2))
|
|
242
|
+
magic2 = struct.pack('BB', int('11111111', 2), int('11111001', 2))
|
|
243
|
+
indices1 = [m.start() for m in re.finditer(magic1, data)]
|
|
244
|
+
indices2 = [m.start() for m in re.finditer(magic2, data)]
|
|
245
|
+
indices = indices1 + indices2
|
|
246
|
+
indices.sort()
|
|
247
|
+
return indices
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
#########################################
|
|
251
|
+
# Source class:
|
|
252
|
+
#########################################
|
|
253
|
+
class FLACSource(StreamingSource):
|
|
254
|
+
def __init__(self, filename, file=None):
|
|
255
|
+
if file is None:
|
|
256
|
+
file = open(filename, 'rb')
|
|
257
|
+
|
|
258
|
+
self.frame_indices = _find_frame_indicies(file.read())
|
|
259
|
+
self.frame_index = 0
|
|
260
|
+
file.seek(0)
|
|
261
|
+
self._file = BitInputStream(file)
|
|
262
|
+
self._filename = filename
|
|
263
|
+
|
|
264
|
+
if file.read(4) != b'fLaC':
|
|
265
|
+
raise FLACDecodeException("Does not appear to be a FLAC file.")
|
|
266
|
+
|
|
267
|
+
samplerate = numchannels = bits_per_sample = numsamples = None
|
|
268
|
+
|
|
269
|
+
last = False
|
|
270
|
+
while not last:
|
|
271
|
+
last = self._file.read_uint(1) != 0
|
|
272
|
+
block_type = self._file.read_uint(7)
|
|
273
|
+
length = self._file.read_uint(24)
|
|
274
|
+
if block_type == 0: # Stream info block
|
|
275
|
+
min_block_size = self._file.read_uint(16) # in samples
|
|
276
|
+
max_block_size = self._file.read_uint(16) # in samples
|
|
277
|
+
min_frame_size = self._file.read_uint(24) # in bytes, 0 == unknown
|
|
278
|
+
max_frame_size = self._file.read_uint(24) # in bytes, 0 == unknown
|
|
279
|
+
samplerate = self._file.read_uint(20)
|
|
280
|
+
numchannels = self._file.read_uint(3) + 1
|
|
281
|
+
bits_per_sample = self._file.read_uint(5) + 1
|
|
282
|
+
numsamples = self._file.read_uint(36)
|
|
283
|
+
self._file.read_uint(128)
|
|
284
|
+
else: # Skip other metadata blocks
|
|
285
|
+
for i in range(length):
|
|
286
|
+
self._file.read_byte()
|
|
287
|
+
|
|
288
|
+
for offset in self.frame_indices:
|
|
289
|
+
if offset < self._file.inp.tell():
|
|
290
|
+
self.frame_indices.remove(offset)
|
|
291
|
+
|
|
292
|
+
if not samplerate:
|
|
293
|
+
raise FLACDecodeException("Stream info metadata block absent")
|
|
294
|
+
if bits_per_sample % 8 != 0:
|
|
295
|
+
raise FLACDecodeException("Sample depth {} not supported".format(bits_per_sample))
|
|
296
|
+
|
|
297
|
+
self._numsamples = numsamples
|
|
298
|
+
self._duration = float(numsamples) / samplerate / 60
|
|
299
|
+
self.audio_format = AudioFormat(numchannels, bits_per_sample, samplerate)
|
|
300
|
+
|
|
301
|
+
self.executor = ProcessPoolExecutor(max_workers=3, initializer=_initializer,
|
|
302
|
+
initargs=(filename, numchannels, bits_per_sample))
|
|
303
|
+
|
|
304
|
+
def __del__(self):
|
|
305
|
+
self._file.close()
|
|
306
|
+
|
|
307
|
+
def get_audio_data(self, num_bytes, compensation_time=0.0):
|
|
308
|
+
|
|
309
|
+
data = b''
|
|
310
|
+
|
|
311
|
+
while not data and (self.frame_index <= len(self.frame_indices)):
|
|
312
|
+
offset = self.frame_indices[self.frame_index]
|
|
313
|
+
future = self.executor.submit(decode_frame, offset)
|
|
314
|
+
data += future.result()
|
|
315
|
+
self.frame_index += 1
|
|
316
|
+
|
|
317
|
+
if not data:
|
|
318
|
+
return None
|
|
319
|
+
|
|
320
|
+
return AudioData(data, len(data), timestamp=0.1, duration=0.5, events=[])
|
|
321
|
+
|
|
322
|
+
def save(self, filename="output.wav"):
|
|
323
|
+
with open(filename, 'wb') as out:
|
|
324
|
+
numsamples = self._numsamples
|
|
325
|
+
numchannels = self.audio_format.channels
|
|
326
|
+
samplerate = self.audio_format.sample_rate
|
|
327
|
+
sample_size = self.audio_format.sample_size
|
|
328
|
+
|
|
329
|
+
# Start writing WAV file headers
|
|
330
|
+
sampledatalen = numsamples * numchannels * (sample_size // 8)
|
|
331
|
+
|
|
332
|
+
out.write(b"RIFF")
|
|
333
|
+
out.write(struct.pack("<I", sampledatalen + 36))
|
|
334
|
+
out.write(b"WAVE")
|
|
335
|
+
out.write(b"fmt ")
|
|
336
|
+
out.write(struct.pack("<IHHIIHH",
|
|
337
|
+
16,
|
|
338
|
+
0x0001,
|
|
339
|
+
numchannels,
|
|
340
|
+
samplerate,
|
|
341
|
+
samplerate * numchannels * (sample_size // 8),
|
|
342
|
+
numchannels * (sample_size // 8),
|
|
343
|
+
sample_size))
|
|
344
|
+
out.write(b"data")
|
|
345
|
+
out.write(struct.pack("<I", sampledatalen))
|
|
346
|
+
|
|
347
|
+
import time
|
|
348
|
+
start = time.time()
|
|
349
|
+
futures = [self.executor.submit(decode_frame, offset) for offset in self.frame_indices]
|
|
350
|
+
print("elapsed future making", time.time() - start)
|
|
351
|
+
|
|
352
|
+
while futures:
|
|
353
|
+
data = futures.pop(0).result()
|
|
354
|
+
out.write(data)
|
|
355
|
+
|
|
356
|
+
def seek(self, timestamp):
|
|
357
|
+
pass
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
#########################################
|
|
361
|
+
# Decoder class:
|
|
362
|
+
#########################################
|
|
363
|
+
class FLACDecoder(MediaDecoder):
|
|
364
|
+
|
|
365
|
+
def get_file_extensions(self):
|
|
366
|
+
return '.flac',
|
|
367
|
+
|
|
368
|
+
def decode(self, file, filename, streaming=True):
|
|
369
|
+
if streaming:
|
|
370
|
+
return FLACSource(filename, file)
|
|
371
|
+
else:
|
|
372
|
+
return StaticSource(FLACSource(filename, file))
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def get_decoders():
|
|
376
|
+
return [FLACDecoder()]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def get_encoders():
|
|
380
|
+
return []
|
|
@@ -245,7 +245,7 @@ class Sprite(event.EventDispatcher):
|
|
|
245
245
|
"""Create a sprite.
|
|
246
246
|
|
|
247
247
|
:Parameters:
|
|
248
|
-
`img` :
|
|
248
|
+
`img` :
|
|
249
249
|
Image or animation to display.
|
|
250
250
|
`x` : int
|
|
251
251
|
X coordinate of the sprite.
|
|
@@ -418,11 +418,7 @@ class Sprite(event.EventDispatcher):
|
|
|
418
418
|
|
|
419
419
|
@property
|
|
420
420
|
def image(self):
|
|
421
|
-
"""Image or animation to display.
|
|
422
|
-
|
|
423
|
-
:type: :py:class:`~pyglet.image.AbstractImage` or
|
|
424
|
-
:py:class:`~pyglet.image.Animation`
|
|
425
|
-
"""
|
|
421
|
+
"""Image or animation to display."""
|
|
426
422
|
if self._animation:
|
|
427
423
|
return self._animation
|
|
428
424
|
return self._texture
|
|
@@ -772,9 +768,16 @@ class Sprite(event.EventDispatcher):
|
|
|
772
768
|
efficiently.
|
|
773
769
|
"""
|
|
774
770
|
ctx = pyglet.graphics.api.core.current_context
|
|
775
|
-
|
|
771
|
+
draw_ctx = pyglet.graphics.draw.DrawContext(
|
|
772
|
+
surface_ctx=ctx,
|
|
773
|
+
backend_ctx=None,
|
|
774
|
+
draw_pass=pyglet.graphics.draw.BatchDrawOptions().resolve(ctx),
|
|
775
|
+
renderer=ctx.renderer,
|
|
776
|
+
)
|
|
777
|
+
draw_ctx.begin()
|
|
778
|
+
self._group.set_state_recursive(draw_ctx)
|
|
776
779
|
self._vertex_list.draw(GeometryMode.POINTS)
|
|
777
|
-
self._group.unset_state_recursive(
|
|
780
|
+
self._group.unset_state_recursive(draw_ctx)
|
|
778
781
|
|
|
779
782
|
def __del__(self):
|
|
780
783
|
try:
|
pyglet/experimental/net.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Experimental networking
|
|
1
|
+
"""Experimental networking.
|
|
2
2
|
|
|
3
3
|
This module contains experiments in making user-friendly Server and Client
|
|
4
4
|
classes that integrate with pyglet's event system. These are very basic,
|
|
@@ -56,7 +56,7 @@ _debug_net = debug_print('debug_net')
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
class Client(_EventDispatcher):
|
|
59
|
-
def __init__(self, address, port):
|
|
59
|
+
def __init__(self, address: str, port: int):
|
|
60
60
|
"""Create a Client connection to a Server."""
|
|
61
61
|
self._socket = _socket.create_connection((address, port))
|
|
62
62
|
self._address = address
|
|
@@ -70,7 +70,7 @@ class Client(_EventDispatcher):
|
|
|
70
70
|
|
|
71
71
|
self._sentinal = object() # poison pill
|
|
72
72
|
|
|
73
|
-
def close(self):
|
|
73
|
+
def close(self) -> None:
|
|
74
74
|
"""Close the connection."""
|
|
75
75
|
self._queue.put(self._sentinal)
|
|
76
76
|
self._socket.shutdown(1)
|
|
@@ -78,15 +78,15 @@ class Client(_EventDispatcher):
|
|
|
78
78
|
self._terminate.set()
|
|
79
79
|
self.dispatch_event('on_disconnect', self)
|
|
80
80
|
|
|
81
|
-
def send(self, message):
|
|
81
|
+
def send(self, message: bytes) -> None:
|
|
82
82
|
"""Queue a message to send.
|
|
83
83
|
|
|
84
84
|
Put a string of bytes into the queue to send.
|
|
85
85
|
raises a `ConnectionError` if the connection
|
|
86
86
|
has been closed or dropped.
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
Args:
|
|
89
|
+
message:
|
|
90
90
|
A string of bytes to send.
|
|
91
91
|
"""
|
|
92
92
|
if self._terminate.is_set():
|
pyglet/experimental/particles.py
CHANGED
|
@@ -393,3 +393,41 @@ class ParticleManager:
|
|
|
393
393
|
batch=self._batch, group=self._group)
|
|
394
394
|
pyglet.clock.schedule_once(self._delete_callback, self.lifespan, emitter)
|
|
395
395
|
return emitter
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
if __name__ == "__main__":
|
|
399
|
+
window = pyglet.window.Window(960, 540, caption="ParticleManager Demo", resizable=True)
|
|
400
|
+
|
|
401
|
+
batch = graphics.Batch()
|
|
402
|
+
|
|
403
|
+
label = pyglet.text.Label("Click and drag.", x=5, y=5, batch=batch)
|
|
404
|
+
|
|
405
|
+
particle_img = image.SolidColorImagePattern((255, 255, 255, 255)).create_image(8, 8)
|
|
406
|
+
manager = ParticleManager(
|
|
407
|
+
particle_img,
|
|
408
|
+
lifespan=1.0,
|
|
409
|
+
count=12,
|
|
410
|
+
velocity=(100.0, 80.0),
|
|
411
|
+
spread=(0.30, 0.30),
|
|
412
|
+
color_start=(255, 200, 80, 220),
|
|
413
|
+
color_end=(255, 40, 20, 0),
|
|
414
|
+
scale_start=(0.4, 0.4),
|
|
415
|
+
scale_end=(1.2, 1.2),
|
|
416
|
+
rotation=0.0,
|
|
417
|
+
batch=batch,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
@window.event
|
|
421
|
+
def on_draw():
|
|
422
|
+
window.clear()
|
|
423
|
+
batch.draw()
|
|
424
|
+
|
|
425
|
+
@window.event
|
|
426
|
+
def on_mouse_press(x, y, button, modifiers):
|
|
427
|
+
manager.create_emitter(x, y)
|
|
428
|
+
|
|
429
|
+
@window.event
|
|
430
|
+
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
|
|
431
|
+
manager.create_emitter(x, y)
|
|
432
|
+
|
|
433
|
+
pyglet.app.run()
|