pyrekordbox 0.2.1__py3-none-any.whl → 0.2.2__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.
- docs/source/formats/anlz.md +178 -7
- docs/source/formats/db6.md +1 -1
- docs/source/index.md +2 -6
- docs/source/quickstart.md +68 -45
- docs/source/tutorial/index.md +1 -1
- pyrekordbox/__init__.py +1 -1
- pyrekordbox/_version.py +2 -2
- pyrekordbox/anlz/file.py +39 -0
- pyrekordbox/anlz/structs.py +3 -5
- pyrekordbox/config.py +71 -27
- pyrekordbox/db6/database.py +260 -33
- pyrekordbox/db6/registry.py +22 -0
- pyrekordbox/db6/tables.py +3 -4
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/METADATA +12 -11
- pyrekordbox-0.2.2.dist-info/RECORD +80 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/top_level.txt +0 -2
- tests/test_config.py +175 -0
- tests/test_db6.py +78 -0
- build/lib/build/lib/docs/source/conf.py +0 -178
- build/lib/build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/build/lib/pyrekordbox/_version.py +0 -16
- build/lib/build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/build/lib/pyrekordbox/config.py +0 -596
- build/lib/build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/build/lib/pyrekordbox/logger.py +0 -23
- build/lib/build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/build/lib/pyrekordbox/utils.py +0 -162
- build/lib/build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/build/lib/tests/__init__.py +0 -3
- build/lib/build/lib/tests/test_anlz.py +0 -206
- build/lib/build/lib/tests/test_db6.py +0 -1039
- build/lib/build/lib/tests/test_mysetting.py +0 -203
- build/lib/build/lib/tests/test_xml.py +0 -629
- build/lib/docs/source/conf.py +0 -178
- build/lib/pyrekordbox/__init__.py +0 -22
- build/lib/pyrekordbox/__main__.py +0 -204
- build/lib/pyrekordbox/_version.py +0 -16
- build/lib/pyrekordbox/anlz/__init__.py +0 -127
- build/lib/pyrekordbox/anlz/file.py +0 -186
- build/lib/pyrekordbox/anlz/structs.py +0 -299
- build/lib/pyrekordbox/anlz/tags.py +0 -508
- build/lib/pyrekordbox/config.py +0 -596
- build/lib/pyrekordbox/db6/__init__.py +0 -45
- build/lib/pyrekordbox/db6/aux_files.py +0 -213
- build/lib/pyrekordbox/db6/database.py +0 -1808
- build/lib/pyrekordbox/db6/registry.py +0 -304
- build/lib/pyrekordbox/db6/tables.py +0 -1618
- build/lib/pyrekordbox/logger.py +0 -23
- build/lib/pyrekordbox/mysettings/__init__.py +0 -32
- build/lib/pyrekordbox/mysettings/file.py +0 -369
- build/lib/pyrekordbox/mysettings/structs.py +0 -282
- build/lib/pyrekordbox/utils.py +0 -162
- build/lib/pyrekordbox/xml.py +0 -1294
- build/lib/tests/__init__.py +0 -3
- build/lib/tests/test_anlz.py +0 -206
- build/lib/tests/test_db6.py +0 -1039
- build/lib/tests/test_mysetting.py +0 -203
- build/lib/tests/test_xml.py +0 -629
- pyrekordbox-0.2.1.dist-info/RECORD +0 -129
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/LICENSE +0 -0
- {pyrekordbox-0.2.1.dist-info → pyrekordbox-0.2.2.dist-info}/WHEEL +0 -0
@@ -1,508 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
# Author: Dylan Jones
|
3
|
-
# Date: 2023-02-01
|
4
|
-
|
5
|
-
import logging
|
6
|
-
from abc import ABC
|
7
|
-
import numpy as np
|
8
|
-
from . import structs
|
9
|
-
|
10
|
-
logger = logging.getLogger(__name__)
|
11
|
-
|
12
|
-
|
13
|
-
class BuildTagLengthError(Exception):
|
14
|
-
def __init__(self, struct, len_data):
|
15
|
-
super().__init__(
|
16
|
-
f"`len_tag` ({struct.len_tag}) of '{struct.type}' does not "
|
17
|
-
f"match the data-length ({len_data})!"
|
18
|
-
)
|
19
|
-
|
20
|
-
|
21
|
-
class AbstractAnlzTag(ABC):
|
22
|
-
"""Abstract base class for struct handlers of Rekordbox analysis files."""
|
23
|
-
|
24
|
-
type: str
|
25
|
-
name: str
|
26
|
-
LEN_HEADER: int = 0 # Expected value of `len_header`
|
27
|
-
LEN_TAG: int = 0 # Expected value of `len_tag`
|
28
|
-
|
29
|
-
def __init__(self, tag_data):
|
30
|
-
self.struct = None
|
31
|
-
if tag_data is not None:
|
32
|
-
self.parse(tag_data)
|
33
|
-
|
34
|
-
@property
|
35
|
-
def content(self):
|
36
|
-
return self.struct.content
|
37
|
-
|
38
|
-
def _check_len_header(self):
|
39
|
-
if self.LEN_HEADER != self.struct.len_header:
|
40
|
-
logger.warning(
|
41
|
-
"`len_header` (%s) of `%s` doesn't match the expected value %s",
|
42
|
-
self.struct.len_header,
|
43
|
-
self.struct.type,
|
44
|
-
self.LEN_HEADER,
|
45
|
-
)
|
46
|
-
|
47
|
-
def _check_len_tag(self):
|
48
|
-
if self.LEN_TAG != self.struct.len_tag:
|
49
|
-
logger.warning(
|
50
|
-
"`len_tag` (%s) of `%s` doesn't match the expected value %s",
|
51
|
-
self.struct.len_tag,
|
52
|
-
self.struct.type,
|
53
|
-
self.LEN_TAG,
|
54
|
-
)
|
55
|
-
|
56
|
-
def check_parse(self):
|
57
|
-
pass
|
58
|
-
|
59
|
-
def parse(self, tag_data):
|
60
|
-
self.struct = structs.AnlzTag.parse(tag_data)
|
61
|
-
if self.LEN_HEADER:
|
62
|
-
self._check_len_header()
|
63
|
-
if self.LEN_TAG:
|
64
|
-
self._check_len_tag()
|
65
|
-
self.check_parse()
|
66
|
-
|
67
|
-
def build(self):
|
68
|
-
data = structs.AnlzTag.build(self.struct)
|
69
|
-
len_data = len(data)
|
70
|
-
if len_data != self.struct.len_tag:
|
71
|
-
raise BuildTagLengthError(self.struct, len_data)
|
72
|
-
return data
|
73
|
-
|
74
|
-
def get(self):
|
75
|
-
return self.struct.content
|
76
|
-
|
77
|
-
def set(self, *args, **kwargs):
|
78
|
-
pass
|
79
|
-
|
80
|
-
def update_len(self):
|
81
|
-
pass
|
82
|
-
|
83
|
-
def __repr__(self):
|
84
|
-
len_header = self.struct.len_header
|
85
|
-
len_tag = self.struct.len_tag
|
86
|
-
return f"{self.__class__.__name__}(len_header={len_header}, len_tag={len_tag})"
|
87
|
-
|
88
|
-
def pformat(self):
|
89
|
-
return str(self.struct)
|
90
|
-
|
91
|
-
|
92
|
-
def _parse_wf_preview(tag):
|
93
|
-
n = len(tag.entries)
|
94
|
-
wf = np.zeros(n, dtype=np.int8)
|
95
|
-
col = np.zeros(n, dtype=np.int8)
|
96
|
-
for i in range(n):
|
97
|
-
data = tag.entries[i]
|
98
|
-
wf[i] = data & 0x1F
|
99
|
-
col[i] = data >> 5
|
100
|
-
return wf, col
|
101
|
-
|
102
|
-
|
103
|
-
class PQTZAnlzTag(AbstractAnlzTag):
|
104
|
-
"""Beat grid struct handler."""
|
105
|
-
|
106
|
-
type = "PQTZ"
|
107
|
-
name = "beat_grid"
|
108
|
-
LEN_HEADER = 24
|
109
|
-
|
110
|
-
@property
|
111
|
-
def count(self):
|
112
|
-
return len(self.content.entries)
|
113
|
-
|
114
|
-
@property
|
115
|
-
def beats(self):
|
116
|
-
return self.get_beats()
|
117
|
-
|
118
|
-
@property
|
119
|
-
def bpms(self):
|
120
|
-
return self.get_bpms()
|
121
|
-
|
122
|
-
@property
|
123
|
-
def bpms_average(self):
|
124
|
-
if len(self.content.entries):
|
125
|
-
return np.mean(self.get_bpms())
|
126
|
-
return 0.0
|
127
|
-
|
128
|
-
@property
|
129
|
-
def bpms_unique(self):
|
130
|
-
return np.unique(self.get_bpms())
|
131
|
-
|
132
|
-
@property
|
133
|
-
def times(self):
|
134
|
-
return self.get_times()
|
135
|
-
|
136
|
-
def get(self):
|
137
|
-
n = len(self.content.entries)
|
138
|
-
beats = np.zeros(n, dtype=np.int8)
|
139
|
-
bpms = np.zeros(n, dtype=np.float64)
|
140
|
-
times = np.zeros(n, dtype=np.float64)
|
141
|
-
for i, entry in enumerate(self.content.entries):
|
142
|
-
_, beat, bpm, time = entry.values()
|
143
|
-
beats[i] = beat
|
144
|
-
bpms[i] = bpm / 100 # BPM is saved as 100 * BPM
|
145
|
-
times[i] = time / 1_000 # Convert milliseconds to seconds
|
146
|
-
return beats, bpms, times
|
147
|
-
|
148
|
-
def get_beats(self):
|
149
|
-
return np.array([entry.beat for entry in self.content.entries], dtype=np.int8)
|
150
|
-
|
151
|
-
def get_bpms(self):
|
152
|
-
return np.array([entry.tempo / 100 for entry in self.content.entries])
|
153
|
-
|
154
|
-
def get_times(self):
|
155
|
-
return np.array([entry.time / 1000 for entry in self.content.entries])
|
156
|
-
|
157
|
-
def set(self, beats, bpms, times):
|
158
|
-
n = len(self.content.entries)
|
159
|
-
n_beats = len(beats)
|
160
|
-
n_bpms = len(bpms)
|
161
|
-
n_times = len(times)
|
162
|
-
if n_bpms != n_beats:
|
163
|
-
raise ValueError(
|
164
|
-
f"Number of bpms not equal to number of beats: {n_bpms} != {n_beats}"
|
165
|
-
)
|
166
|
-
if n_times != n_beats:
|
167
|
-
raise ValueError(
|
168
|
-
f"Number of times not equal to number of beats: {n_bpms} != {n_times}"
|
169
|
-
)
|
170
|
-
|
171
|
-
# For now only values of existing beats can be set
|
172
|
-
if n_beats != n:
|
173
|
-
raise ValueError(
|
174
|
-
f"Number of beats not equal to current content length: {n_beats} != {n}"
|
175
|
-
)
|
176
|
-
|
177
|
-
for i, (beat, bpm, t) in enumerate(zip(beats, bpms, times)):
|
178
|
-
data = {"beat": int(beat), "tempo": int(100 * bpm), "time": int(1000 * t)}
|
179
|
-
self.content.entries[i].update(data)
|
180
|
-
|
181
|
-
def set_beats(self, beats):
|
182
|
-
n = len(self.content.entries)
|
183
|
-
n_new = len(beats)
|
184
|
-
if n_new != n:
|
185
|
-
raise ValueError(
|
186
|
-
f"Number of beats not equal to current content length: {n_new} != {n}"
|
187
|
-
)
|
188
|
-
|
189
|
-
for i, beat in enumerate(beats):
|
190
|
-
self.content.entries[i].beat = beat
|
191
|
-
|
192
|
-
def set_bpms(self, bpms):
|
193
|
-
n = len(self.content.entries)
|
194
|
-
n_new = len(bpms)
|
195
|
-
if n_new != n:
|
196
|
-
raise ValueError(
|
197
|
-
f"Number of bpms not equal to current content length: {n_new} != {n}"
|
198
|
-
)
|
199
|
-
|
200
|
-
for i, bpm in enumerate(bpms):
|
201
|
-
self.content.entries[i].tempo = int(bpm * 100)
|
202
|
-
|
203
|
-
def set_times(self, times):
|
204
|
-
n = len(self.content.entries)
|
205
|
-
n_new = len(times)
|
206
|
-
if n_new != n:
|
207
|
-
raise ValueError(
|
208
|
-
f"Number of times not equal to current content length: {n_new} != {n}"
|
209
|
-
)
|
210
|
-
|
211
|
-
for i, t in enumerate(times):
|
212
|
-
self.content.entries[i].time = int(1000 * t)
|
213
|
-
|
214
|
-
def check_parse(self):
|
215
|
-
assert self.struct.content.entry_count == len(self.struct.content.entries)
|
216
|
-
|
217
|
-
def update_len(self):
|
218
|
-
self.struct.len_tag = self.struct.len_header + 8 * len(self.content.entries)
|
219
|
-
|
220
|
-
|
221
|
-
class PQT2AnlzTag(AbstractAnlzTag):
|
222
|
-
"""Extended (nxs2) beat grid struct handler."""
|
223
|
-
|
224
|
-
type = "PQT2"
|
225
|
-
name = "beat_grid2"
|
226
|
-
LEN_HEADER = 56
|
227
|
-
|
228
|
-
count = 2
|
229
|
-
|
230
|
-
@property
|
231
|
-
def beats(self):
|
232
|
-
return self.get_beats()
|
233
|
-
|
234
|
-
@property
|
235
|
-
def bpms(self):
|
236
|
-
return self.get_bpms()
|
237
|
-
|
238
|
-
@property
|
239
|
-
def times(self):
|
240
|
-
return self.get_times()
|
241
|
-
|
242
|
-
@property
|
243
|
-
def beat_grid_count(self):
|
244
|
-
return self.content.entry_count
|
245
|
-
|
246
|
-
@property
|
247
|
-
def bpms_unique(self):
|
248
|
-
return np.unique(self.get_bpms())
|
249
|
-
|
250
|
-
def check_parse(self):
|
251
|
-
len_beats = self.struct.content.entry_count
|
252
|
-
if len_beats:
|
253
|
-
expected = self.struct.len_tag - self.struct.len_header
|
254
|
-
actual = 2 * len(self.content.entries) # each entry consist of 2 bytes
|
255
|
-
assert actual == expected, f"{actual} != {expected}"
|
256
|
-
|
257
|
-
def get(self):
|
258
|
-
n = len(self.content.bpm)
|
259
|
-
beats = np.zeros(n, dtype=np.int8)
|
260
|
-
bpms = np.zeros(n, dtype=np.float64)
|
261
|
-
times = np.zeros(n, dtype=np.float64)
|
262
|
-
for i, entry in enumerate(self.content.bpm):
|
263
|
-
_, beat, bpm, time = entry.values()
|
264
|
-
beats[i] = beat
|
265
|
-
bpms[i] = bpm / 100 # BPM is saved as 100 * BPM
|
266
|
-
times[i] = time / 1_000 # Convert milliseconds to seconds
|
267
|
-
return beats, bpms, times
|
268
|
-
|
269
|
-
def get_beats(self):
|
270
|
-
return np.array([entry.beat for entry in self.content.bpm], dtype=np.int8)
|
271
|
-
|
272
|
-
def get_bpms(self):
|
273
|
-
return np.array([entry.tempo / 100 for entry in self.content.bpm])
|
274
|
-
|
275
|
-
def get_times(self):
|
276
|
-
return np.array([entry.time / 1000 for entry in self.content.bpm])
|
277
|
-
|
278
|
-
def get_beat_grid(self):
|
279
|
-
return np.array([entry.beat for entry in self.content.entries], dtype=np.int8)
|
280
|
-
|
281
|
-
def set_beats(self, beats):
|
282
|
-
for i, beat in enumerate(beats):
|
283
|
-
self.content.bpm[i].beat = beat
|
284
|
-
|
285
|
-
def set_bpms(self, bpms):
|
286
|
-
for i, bpm in enumerate(bpms):
|
287
|
-
self.content.bpm[i].bpm = int(bpm * 100)
|
288
|
-
|
289
|
-
def set_times(self, times):
|
290
|
-
for i, t in enumerate(times):
|
291
|
-
self.content.bpm[i].time = int(1000 * t)
|
292
|
-
|
293
|
-
def build(self):
|
294
|
-
data = structs.AnlzTag.build(self.struct)
|
295
|
-
if self.struct.content.entry_count == 0:
|
296
|
-
data = data[: self.struct.len_tag]
|
297
|
-
|
298
|
-
len_data = len(data)
|
299
|
-
if len_data != self.struct.len_tag:
|
300
|
-
raise BuildTagLengthError(self.struct, len_data)
|
301
|
-
return data
|
302
|
-
|
303
|
-
|
304
|
-
class PCOBAnlzTag(AbstractAnlzTag):
|
305
|
-
"""Cue list struct handler."""
|
306
|
-
|
307
|
-
type = "PCOB"
|
308
|
-
name = "cue_list"
|
309
|
-
LEN_HEADER = 24
|
310
|
-
|
311
|
-
|
312
|
-
class PCO2AnlzTag(AbstractAnlzTag):
|
313
|
-
"""Extended (nxs2) cue list struct handler."""
|
314
|
-
|
315
|
-
type = "PCO2"
|
316
|
-
name = "cue_list2"
|
317
|
-
LEN_HEADER = 20
|
318
|
-
|
319
|
-
|
320
|
-
class PPTHAnlzTag(AbstractAnlzTag):
|
321
|
-
"""Path struct handler."""
|
322
|
-
|
323
|
-
type = "PPTH"
|
324
|
-
name = "path"
|
325
|
-
LEN_HEADER = 16
|
326
|
-
|
327
|
-
@property
|
328
|
-
def path(self):
|
329
|
-
return self.content.path
|
330
|
-
|
331
|
-
def get(self):
|
332
|
-
return self.content.path
|
333
|
-
|
334
|
-
def set(self, path):
|
335
|
-
path = path.replace("\\", "/")
|
336
|
-
len_path = len(path.encode("utf-16-be")) + 2
|
337
|
-
self.content.path = path
|
338
|
-
self.content.len_path = len_path
|
339
|
-
|
340
|
-
def update_len(self):
|
341
|
-
self.struct.len_tag = self.struct.len_header + self.content.len_path
|
342
|
-
|
343
|
-
|
344
|
-
class PVBRAnlzTag(AbstractAnlzTag):
|
345
|
-
"""VBR struct handler."""
|
346
|
-
|
347
|
-
type = "PVBR"
|
348
|
-
name = "vbr"
|
349
|
-
LEN_HEADER = 16
|
350
|
-
LEN_TAG = 1620
|
351
|
-
|
352
|
-
def get(self):
|
353
|
-
return np.array(self.content.idx)
|
354
|
-
|
355
|
-
|
356
|
-
class PSSIAnlzTag(AbstractAnlzTag):
|
357
|
-
"""Song structure struct handler."""
|
358
|
-
|
359
|
-
type = "PSSI"
|
360
|
-
name = "structure"
|
361
|
-
LEN_HEADER = 32
|
362
|
-
|
363
|
-
|
364
|
-
class PWAVAnlzTag(AbstractAnlzTag):
|
365
|
-
"""Waveform preview struct handler."""
|
366
|
-
|
367
|
-
type = "PWAV"
|
368
|
-
name = "wf_preview"
|
369
|
-
LEN_HEADER = 20
|
370
|
-
|
371
|
-
def get(self):
|
372
|
-
return _parse_wf_preview(self.content)
|
373
|
-
|
374
|
-
|
375
|
-
class PWV2AnlzTag(AbstractAnlzTag):
|
376
|
-
"""Tiny waveform preview struct handler."""
|
377
|
-
|
378
|
-
type = "PWV2"
|
379
|
-
name = "wf_tiny_preview"
|
380
|
-
LEN_HEADER = 20
|
381
|
-
|
382
|
-
def get(self):
|
383
|
-
return _parse_wf_preview(self.content)
|
384
|
-
|
385
|
-
|
386
|
-
class PWV3AnlzTag(AbstractAnlzTag):
|
387
|
-
"""Waveform detail struct handler."""
|
388
|
-
|
389
|
-
type = "PWV3"
|
390
|
-
name = "wf_detail"
|
391
|
-
LEN_HEADER = 24
|
392
|
-
|
393
|
-
def get(self):
|
394
|
-
return _parse_wf_preview(self.content)
|
395
|
-
|
396
|
-
|
397
|
-
class PWV4AnlzTag(AbstractAnlzTag):
|
398
|
-
"""Waveform color preview struct handler."""
|
399
|
-
|
400
|
-
type = "PWV4"
|
401
|
-
name = "wf_color"
|
402
|
-
LEN_HEADER = 24
|
403
|
-
|
404
|
-
def get(self):
|
405
|
-
num_entries = self.content.len_entries
|
406
|
-
data = self.content.entries
|
407
|
-
ws, hs = 1, 1
|
408
|
-
w = int(num_entries / ws)
|
409
|
-
# parse color and blue waveforms
|
410
|
-
col_color = np.zeros((num_entries, 2, 3), dtype=np.int64)
|
411
|
-
col_blues = np.zeros((num_entries, 2, 3), dtype=np.int64)
|
412
|
-
heights = np.zeros((num_entries, 2), dtype=np.int64)
|
413
|
-
for x in range(w):
|
414
|
-
# d0 = data[x * ws * 6 + 0] # unknown?
|
415
|
-
d1 = data[x * ws * 6 + 1] # some kind of luminance boost?
|
416
|
-
d2 = data[x * ws * 6 + 2] & 0x7F # inverse intensity for blue waveform
|
417
|
-
d3 = data[x * ws * 6 + 3] & 0x7F # red
|
418
|
-
d4 = data[x * ws * 6 + 4] & 0x7F # green
|
419
|
-
d5 = data[x * ws * 6 + 5] & 0x7F # blue and height of front waveform
|
420
|
-
bh = int(max(d2, d3, d4) / hs) # back height is max of d3, d4 probably d2?
|
421
|
-
fh = int(d5 / hs) # front height is d5
|
422
|
-
fl = 32 # front luminosity increase (arbitrary)
|
423
|
-
# waveform heights
|
424
|
-
heights[x] = fh, bh
|
425
|
-
# color waveform
|
426
|
-
col = np.array([d3, d4, d5]) * (d1 / 127)
|
427
|
-
col_color[x] = col, col + fl
|
428
|
-
# blue waveform
|
429
|
-
col = 95 - d2 * np.array([1.0, 0.5, 0.25])
|
430
|
-
col_blues[x] = col, col + fl
|
431
|
-
return heights, col_color, col_blues
|
432
|
-
|
433
|
-
|
434
|
-
class PWV5AnlzTag(AbstractAnlzTag):
|
435
|
-
"""Waveform color detail struct handler."""
|
436
|
-
|
437
|
-
type = "PWV5"
|
438
|
-
name = "wf_color_detail"
|
439
|
-
LEN_HEADER = 24
|
440
|
-
|
441
|
-
def get(self):
|
442
|
-
"""Parse the Waveform Color Detail Tag (PWV5)
|
443
|
-
|
444
|
-
The format of the entries is:
|
445
|
-
|
446
|
-
f e d c b a 9 8 7 6 5 4 3 2 1 0
|
447
|
-
│ red │ green │ blue │ height │00 00│
|
448
|
-
"""
|
449
|
-
rmask = 0xE000 # 111 000 000 00000 00
|
450
|
-
gmask = 0x1C00 # 000 111 000 00000 00
|
451
|
-
bmask = 0x380 # 000 000 111 00000 00
|
452
|
-
hmask = 0x7C # 000 000 000 11111 00
|
453
|
-
|
454
|
-
n = self.content.len_entries
|
455
|
-
heights = np.zeros(n, dtype=np.int64)
|
456
|
-
colors = np.zeros((n, 3), dtype=np.int64)
|
457
|
-
for i, x in enumerate(self.content.entries):
|
458
|
-
red = (x & rmask) >> 12
|
459
|
-
green = (x & gmask) >> 10
|
460
|
-
blue = (x & bmask) >> 7
|
461
|
-
heights[i] = (x & hmask) >> 2
|
462
|
-
colors[i] = red, green, blue
|
463
|
-
# Normalize heights to 1:
|
464
|
-
heights = heights / 31
|
465
|
-
return heights, colors
|
466
|
-
|
467
|
-
|
468
|
-
class PWV6AnlzTag(AbstractAnlzTag):
|
469
|
-
"""PWV6 struct handler."""
|
470
|
-
|
471
|
-
type = "PWV6"
|
472
|
-
name = type
|
473
|
-
LEN_HEADER = 20
|
474
|
-
|
475
|
-
|
476
|
-
class PWV7AnlzTag(AbstractAnlzTag):
|
477
|
-
"""PWV7 struct handler."""
|
478
|
-
|
479
|
-
type = "PWV7"
|
480
|
-
name = type
|
481
|
-
LEN_HEADER = 24
|
482
|
-
|
483
|
-
|
484
|
-
class PWVCAnlzTag(AbstractAnlzTag):
|
485
|
-
"""PWVC struct handler."""
|
486
|
-
|
487
|
-
type = "PWVC"
|
488
|
-
name = type
|
489
|
-
LEN_HEADER = 14
|
490
|
-
|
491
|
-
|
492
|
-
TAGS = {
|
493
|
-
"PQTZ": PQTZAnlzTag,
|
494
|
-
"PQT2": PQT2AnlzTag,
|
495
|
-
"PCOB": PCOBAnlzTag, # seen in both DAT and EXT files
|
496
|
-
"PCO2": PCO2AnlzTag, # seen in EXT files
|
497
|
-
"PPTH": PPTHAnlzTag,
|
498
|
-
"PVBR": PVBRAnlzTag,
|
499
|
-
"PSSI": PSSIAnlzTag, # seen in EXT files
|
500
|
-
"PWAV": PWAVAnlzTag,
|
501
|
-
"PWV2": PWV2AnlzTag,
|
502
|
-
"PWV3": PWV3AnlzTag, # seen in EXT files
|
503
|
-
"PWV4": PWV4AnlzTag, # seen in EXT files
|
504
|
-
"PWV5": PWV5AnlzTag, # seen in EXT files
|
505
|
-
"PWV6": PWV6AnlzTag, # seen in 2EX files
|
506
|
-
"PWV7": PWV7AnlzTag, # seen in 2EX files
|
507
|
-
"PWVC": PWVCAnlzTag, # seen in 2EX files
|
508
|
-
}
|