auto-editor 27.0.0__py3-none-any.whl → 27.1.1__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.
@@ -1,56 +1,54 @@
1
- auto_editor/__init__.py,sha256=KViW4EBA7GbEItgvzRFMRG3Xq3ASIbwCSsNE9WwakVc,23
2
- auto_editor/__main__.py,sha256=iKfQKFOQDv0-uZecLzfb8A-0k5dAxxe94FaIIEayGXc,15575
1
+ auto_editor/__init__.py,sha256=0ioEHfOkA9Eoup9m_rzaYLCKq4b4PYoylPtUx3iCRPE,23
2
+ auto_editor/__main__.py,sha256=WfNtjKwx5fDMPpfSNLarigXD3Jp0My98FpzQqSAxQZ8,15807
3
3
  auto_editor/analyze.py,sha256=CeJG0LI9wXZk1R-QPrNGPS4za-_Avd8y7H-D437DqLg,12300
4
- auto_editor/edit.py,sha256=9rJC6IY-jMdQDolJBxfzng43ddYQpC8zuZQHLzEct8s,20698
5
- auto_editor/ffwrapper.py,sha256=b2XDsJMmBnYtRAGrIjqhYH909GNo30tMW3FCZUhhZ5k,4781
4
+ auto_editor/edit.py,sha256=qeuStjcBbRVhLweap_pxxAfME3Jh6tHyxywOjrYJ_ts,19125
5
+ auto_editor/ffwrapper.py,sha256=Wet6B5nohgnjpBX7o20Zq0rYr-H9mUuOqHrbQAPPj38,5128
6
6
  auto_editor/help.py,sha256=CzfDTsL4GuGu596ySHKj_wKnxGR9h8B0KUdkZpo33oE,8044
7
7
  auto_editor/json.py,sha256=8IVhZJSLx2IVqJsbR5YKDvbHOhgIOvdQmYNpMdMG_xA,9332
8
- auto_editor/make_layers.py,sha256=VKdBj6Kbe5CAoDIDK_MdqHVu2TXZ0sjeEbqyFTA27gI,9617
9
- auto_editor/output.py,sha256=YgkZw0WyVfcTYH-4j6kYfvB6A1BjxGLmfOQVFi-QK_o,2561
8
+ auto_editor/make_layers.py,sha256=nSEeCHysMot2eze23q05g2HFDuskN_4Jk108xlk2Rw8,10102
10
9
  auto_editor/preview.py,sha256=cqQdozM2IB-5qXHNxeqiSrSdEIzlMfjD4SU-NX9sYZ0,3052
11
- auto_editor/timeline.py,sha256=f1cxAhduoVHBTRgRwGqI3BKBb31aAVrWkEahH5gJ-0o,8379
10
+ auto_editor/timeline.py,sha256=wUduvIB7JRoV_kswSBWJar_aNJH6i-RO917xrON6lIM,9521
12
11
  auto_editor/vanparse.py,sha256=Ug5A2QaRqGiw4l55Z_h9T2QU1x0WqRibR7yY5rQ0WTk,10002
13
- auto_editor/wavfile.py,sha256=afFfje8cK9lFjIkYoBbQHfvQIpsEPxWvspCtFhUKlAw,9499
14
12
  auto_editor/cmds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
13
  auto_editor/cmds/cache.py,sha256=bViYbtVXefTeEIUvSanDfA6cG35ep1N_Jvtz7ZjgIkY,1959
16
- auto_editor/cmds/desc.py,sha256=GDrKJYiHMaeTrplZAceXl1JwoqD78XsV2_5lc0Xd7po,869
17
- auto_editor/cmds/info.py,sha256=Af2aa9vW_bNfFGbRgQMqc7jj17skC6koE0XL3aF0jl0,7005
18
- auto_editor/cmds/levels.py,sha256=EQ2hfRTe6779OtyzPIBZc5g3WhMZRddVkQWSCVV7UlE,5635
14
+ auto_editor/cmds/desc.py,sha256=DSAWPKt8PS9soBTgzpsFIEWoTe4gPTWwjXpNL-p3WsI,866
15
+ auto_editor/cmds/info.py,sha256=VA2WkTBbQPvrVHjDaJyqryoVMtiiN6cguiUMdWgBJfU,7002
16
+ auto_editor/cmds/levels.py,sha256=2Hbvoy6wMbRRoHdZf25PMqq1uvhRKyPcITNPMpZFo7s,5632
19
17
  auto_editor/cmds/palet.py,sha256=ONzTqemaQq9YEfIOsDRNnwzfqnEMUMSXIQrETxyroRU,749
20
- auto_editor/cmds/repl.py,sha256=8DgMw-XyfR5XctSmwtk4_1-zxs3ooMs72BfMRlVqLvY,3412
18
+ auto_editor/cmds/repl.py,sha256=HSUTDaVykPb5Bd-v_jz_8R7HvFmKOcT_ZVmP-d0AbUY,3247
21
19
  auto_editor/cmds/subdump.py,sha256=kHg8nfUi6I6VeJjEgMxupPa666qsYUh7ZEUxint7Gqo,2443
22
- auto_editor/cmds/test.py,sha256=DK7T5BEtMhfrsTJSmXN_tlBKgHdajS-bICiGttq8M0c,27416
20
+ auto_editor/cmds/test.py,sha256=UNN1r6J69-woqA6QU7TeY0WlPxukQzwCRl5DGBTLK8g,28837
23
21
  auto_editor/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- auto_editor/formats/fcp11.py,sha256=sqjC36jI47ICPLjZJYiqGwY7foOnWOiNjkPFLdgSnI4,5208
25
- auto_editor/formats/fcp7.py,sha256=x5cagTzGCAW3i3M6m7TZC1h8gLfSmX1UK-iiDuCpdfs,20289
26
- auto_editor/formats/json.py,sha256=_dPrNr1ZC8kfSHRinG0rFudv1XFsZf2VlGAl084lMQ8,7663
22
+ auto_editor/formats/fcp11.py,sha256=uaJbLiwiVxqoVy5JCA14wZj-m1wqZIzWh7rS-BsnSQM,5219
23
+ auto_editor/formats/fcp7.py,sha256=Q_raDxOBqo8QcnhaiXrIFPB-Un7wUvQ4SYt6KALOI1s,19212
24
+ auto_editor/formats/json.py,sha256=UUBhFR_79vn4Lxu73B0cVBFgw4qytrmMP-TiCmDFMd0,7666
27
25
  auto_editor/formats/shotcut.py,sha256=-ES854LLFCMCBe100JRJedDmuk8zPev17aQMTrzPv-g,4923
28
26
  auto_editor/formats/utils.py,sha256=LYXDiqOk9WwUorLGw2D0M7In9BNDkoKikNawuks7hqE,1648
29
27
  auto_editor/lang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
28
  auto_editor/lang/libintrospection.py,sha256=6H1rGp0wqaCud5IPaoEmzULGnYt6ec7_0h32ATcw2oY,261
31
29
  auto_editor/lang/libmath.py,sha256=z33A161Oe6vYYK7R6pgYjdZZe63dQkN38Qf36TL3prg,847
32
30
  auto_editor/lang/palet.py,sha256=RQjyIZMJSWnzDkHTu-5mt74o9_4zO4VrcH-wLojCF7A,24113
33
- auto_editor/lang/stdenv.py,sha256=Acf9CVXzglh3KgsJXrO7IyMMjTY5jqpz5u0QADXtDHA,44127
31
+ auto_editor/lang/stdenv.py,sha256=o7kFu7EbaH71XPFGxJUXYGxSeZ8O3i1_C5Hmi9uya4Q,44150
34
32
  auto_editor/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
33
  auto_editor/lib/contracts.py,sha256=lExGQymcQUmwG5lC1lO4qm4GY8W0q_yzK_miTaAoPA4,7586
36
34
  auto_editor/lib/data_structs.py,sha256=Hnzl5gWvo-geTU0g-lGejj6HQW3VvPv0NBEj2XoGskY,7089
37
35
  auto_editor/lib/err.py,sha256=UlszQJdzMZwkbT8x3sY4GkCV_5x9yrd6uVVUzvA8iiI,35
38
36
  auto_editor/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- auto_editor/render/audio.py,sha256=7iQMtex8hzzLH80pwLD_nwN_ZH3GHjWRmER45ZVEzPk,12646
37
+ auto_editor/render/audio.py,sha256=ejV_NIvrmxMJt6mf7nPNIzlPHOumNhcIO2J-irHO-k8,17427
40
38
  auto_editor/render/subtitle.py,sha256=F27T8OsAojUIGTGBWqTdH36h0BnHXSExxIqzOtqyZoE,6129
41
- auto_editor/render/video.py,sha256=g2TbuCNzhbE8KsS-_3XRLLdmQKFROdwfkymwIbGmtqc,12012
39
+ auto_editor/render/video.py,sha256=uIrYzF4bDZ3vwfX2F6TdR6F73GI4yruGssto9xEQ-AA,11999
42
40
  auto_editor/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
41
  auto_editor/utils/bar.py,sha256=Ky9JRf37JTgLyvNuIXDfucaUE8H1vBbCqKLjttmsmmo,4156
44
42
  auto_editor/utils/chunks.py,sha256=J-eGKtEz68gFtRrj1kOSgH4Tj_Yz6prNQ7Xr-d9NQJw,52
45
43
  auto_editor/utils/cmdkw.py,sha256=aUGBvBel2Ko1o6Rwmr4rEL-BMc5hEnzYLbyZ1GeJdcY,5729
46
- auto_editor/utils/container.py,sha256=UPqlAos7UIf_ARIAbB3wuaSv1iNLFgy22YFGK8559yM,2487
47
- auto_editor/utils/func.py,sha256=C8ucgsSEzPyBc-8obhsCXd_uQW0cnCdBn1KVxB7FHjU,2747
44
+ auto_editor/utils/container.py,sha256=CNHChHbhzIrjmDdWw6UzMqscrr9u7A-ZqKWejGjJwYE,2628
45
+ auto_editor/utils/func.py,sha256=ODyjXnzSDatEu08w398K8_xBKYdXMY3IPHiJpGRZDyQ,3250
48
46
  auto_editor/utils/log.py,sha256=wPNf6AabV-0cnoS_bPLv1Lh7llQBtNqPKeh07einOuc,3701
49
47
  auto_editor/utils/types.py,sha256=j2hd4zMQ9EftDy41Ji2_PFru_7HEZObd9yKA0BJxFaY,7616
50
- auto_editor-27.0.0.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
48
+ auto_editor-27.1.1.dist-info/licenses/LICENSE,sha256=yiq99pWITHfqS0pbZMp7cy2dnbreTuvBwudsU-njvIM,1210
51
49
  docs/build.py,sha256=g1uc1H9T_naGaermUiVMMwUpbT0IWElRhjgT0fvCh8w,1914
52
- auto_editor-27.0.0.dist-info/METADATA,sha256=VhQA-UaBuv5unUT8MUVkQ_oVwGisctF3BS5tTSTkDfA,6165
53
- auto_editor-27.0.0.dist-info/WHEEL,sha256=tTnHoFhvKQHCh4jz3yCn0WPTYIy7wXx3CJtJ7SJGV7c,91
54
- auto_editor-27.0.0.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
55
- auto_editor-27.0.0.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
56
- auto_editor-27.0.0.dist-info/RECORD,,
50
+ auto_editor-27.1.1.dist-info/METADATA,sha256=ri7aLym52FDEnnDR7czUR3xqZ025BLmepV0Em6GXjQc,6176
51
+ auto_editor-27.1.1.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
52
+ auto_editor-27.1.1.dist-info/entry_points.txt,sha256=UAsTc7qJQbnAzHd7KWg-ALo_X9Hj2yDs3M9I2DV3eyI,212
53
+ auto_editor-27.1.1.dist-info/top_level.txt,sha256=jBV5zlbWRbKOa-xaWPvTD45QL7lGExx2BDzv-Ji4dTw,17
54
+ auto_editor-27.1.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (77.0.1)
2
+ Generator: setuptools (80.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
auto_editor/output.py DELETED
@@ -1,86 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os.path
4
- from dataclasses import dataclass, field
5
-
6
- import bv
7
- from bv.audio.resampler import AudioResampler
8
-
9
- from auto_editor.ffwrapper import FileInfo
10
- from auto_editor.utils.bar import Bar
11
- from auto_editor.utils.log import Log
12
- from auto_editor.utils.types import split_num_str
13
-
14
-
15
- def parse_bitrate(input_: str, log: Log) -> int:
16
- try:
17
- val, unit = split_num_str(input_)
18
- except Exception as e:
19
- log.error(e)
20
-
21
- if unit.lower() == "k":
22
- return int(val * 1000)
23
- if unit == "M":
24
- return int(val * 1_000_000)
25
- if unit == "G":
26
- return int(val * 1_000_000_000)
27
- if unit == "":
28
- return int(val)
29
-
30
- log.error(f"Unknown bitrate: {input_}")
31
-
32
-
33
- @dataclass(slots=True)
34
- class Ensure:
35
- _bar: Bar
36
- _sr: int
37
- log: Log
38
- _audios: list[tuple[FileInfo, int]] = field(default_factory=list)
39
-
40
- def audio(self, src: FileInfo, stream: int) -> str:
41
- try:
42
- label = self._audios.index((src, stream))
43
- first_time = False
44
- except ValueError:
45
- self._audios.append((src, stream))
46
- label = len(self._audios) - 1
47
- first_time = True
48
-
49
- out_path = os.path.join(self.log.temp, f"{label:x}.wav")
50
-
51
- if first_time:
52
- sample_rate = self._sr
53
- bar = self._bar
54
- self.log.debug(f"Making external audio: {out_path}")
55
-
56
- in_container = bv.open(src.path, "r")
57
- out_container = bv.open(
58
- out_path, "w", format="wav", options={"rf64": "always"}
59
- )
60
- astream = in_container.streams.audio[stream]
61
-
62
- if astream.duration is None or astream.time_base is None:
63
- dur = 1.0
64
- else:
65
- dur = float(astream.duration * astream.time_base)
66
-
67
- bar.start(dur, "Extracting audio")
68
-
69
- output_astream = out_container.add_stream(
70
- "pcm_s16le", layout="stereo", rate=sample_rate
71
- )
72
- resampler = AudioResampler(format="s16", layout="stereo", rate=sample_rate)
73
- for i, frame in enumerate(in_container.decode(astream)):
74
- if i % 1500 == 0 and frame.time is not None:
75
- bar.tick(frame.time)
76
-
77
- for new_frame in resampler.resample(frame):
78
- out_container.mux(output_astream.encode(new_frame))
79
-
80
- out_container.mux(output_astream.encode(None))
81
-
82
- out_container.close()
83
- in_container.close()
84
- bar.end()
85
-
86
- return out_path
auto_editor/wavfile.py DELETED
@@ -1,310 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import io
4
- import struct
5
- import sys
6
- from typing import TYPE_CHECKING, Literal
7
-
8
- import numpy as np
9
-
10
- PCM = 0x0001
11
- IEEE_FLOAT = 0x0003
12
- EXTENSIBLE = 0xFFFE
13
-
14
- AudioData = np.memmap | np.ndarray
15
- Endian = Literal[">", "<"] # Big Endian, Little Endian
16
- ByteOrd = Literal["big", "little"]
17
-
18
- if TYPE_CHECKING:
19
- Reader = io.BufferedReader | io.BytesIO
20
- Writer = io.BufferedWriter | io.BytesIO
21
-
22
-
23
- class WavError(Exception):
24
- pass
25
-
26
-
27
- def _read_fmt_chunk(
28
- fid: Reader, bytes_order: ByteOrd
29
- ) -> tuple[int, int, int, int, int]:
30
- size = int.from_bytes(fid.read(4), bytes_order)
31
-
32
- if size < 16:
33
- raise WavError("Binary structure of wave file is not compliant")
34
-
35
- format_tag = int.from_bytes(fid.read(2), bytes_order)
36
- channels = int.from_bytes(fid.read(2), bytes_order)
37
- sr = int.from_bytes(fid.read(4), bytes_order)
38
- fid.read(4) # This is bitrate but we don't need it
39
- block_align = int.from_bytes(fid.read(2), bytes_order)
40
- bit_depth = int.from_bytes(fid.read(2), bytes_order)
41
- bytes_read = 16
42
-
43
- if format_tag == EXTENSIBLE and size >= 18:
44
- ext_chunk_size = int.from_bytes(fid.read(2), bytes_order)
45
- bytes_read += 2
46
- if ext_chunk_size >= 22:
47
- extensible_chunk_data = fid.read(22)
48
- bytes_read += 22
49
- raw_guid = extensible_chunk_data[6:22]
50
-
51
- if bytes_order == "big":
52
- tail = b"\x00\x00\x00\x10\x80\x00\x00\xaa\x00\x38\x9b\x71"
53
- else:
54
- tail = b"\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71"
55
- if raw_guid.endswith(tail):
56
- format_tag = int.from_bytes(raw_guid[:4], bytes_order)
57
- else:
58
- raise WavError("Binary structure of wave file is not compliant")
59
-
60
- if format_tag not in {PCM, IEEE_FLOAT}:
61
- raise WavError(
62
- f"Encountered unknown format tag: {format_tag:#06x}, while reading fmt chunk."
63
- )
64
-
65
- # move file pointer to next chunk
66
- if size > bytes_read:
67
- fid.read(size - bytes_read)
68
-
69
- # fmt should always be 16, 18 or 40, but handle it just in case
70
- _handle_pad_byte(fid, size)
71
-
72
- return format_tag, channels, sr, block_align, bit_depth
73
-
74
-
75
- def _read_data_chunk(
76
- fid: Reader,
77
- format_tag: int,
78
- channels: int,
79
- bit_depth: int,
80
- en: Endian,
81
- block_align: int,
82
- data_size: int | None,
83
- ) -> AudioData:
84
- bytes_order: ByteOrd = "big" if en == ">" else "little"
85
- size = int.from_bytes(fid.read(4), bytes_order)
86
-
87
- if data_size is not None:
88
- # size is only 32-bits here, so get real size from header.
89
- size = data_size
90
-
91
- bytes_per_sample = block_align // channels
92
-
93
- if bytes_per_sample in {3, 5, 6, 7}:
94
- raise WavError(f"Unsupported bytes per sample: {bytes_per_sample}")
95
-
96
- if format_tag == PCM:
97
- if 1 <= bit_depth <= 8:
98
- dtype = "u1" # WAVs of 8-bit integer or less are unsigned
99
- elif bit_depth <= 64:
100
- dtype = f"{en}i{bytes_per_sample}"
101
- else:
102
- raise WavError(
103
- f"Unsupported bit depth: the WAV file has {bit_depth}-bit integer data."
104
- )
105
- elif format_tag == IEEE_FLOAT:
106
- if bit_depth in {32, 64}:
107
- dtype = f"{en}f{bytes_per_sample}"
108
- else:
109
- raise WavError(
110
- f"Unsupported bit depth: the WAV file has {bit_depth}-bit floating-point data."
111
- )
112
- else:
113
- raise WavError(
114
- f"Unknown wave file format: {format_tag:#06x}. Supported formats: PCM, IEEE_FLOAT"
115
- )
116
- if size % 2 == 0:
117
- n_samples = size // block_align
118
- else:
119
- n_samples = (size - 1) // block_align
120
-
121
- if isinstance(fid, io.BufferedReader):
122
- data: AudioData = np.memmap(
123
- fid, dtype=dtype, mode="c", offset=fid.tell(), shape=(n_samples, channels)
124
- )
125
- fid.seek(size, 1)
126
- else:
127
- bytes_per_sample = np.dtype(dtype).itemsize
128
- buffer = fid.read(n_samples * channels * bytes_per_sample)
129
- data = np.frombuffer(buffer, dtype=dtype).reshape((n_samples, channels))
130
-
131
- _handle_pad_byte(fid, size)
132
-
133
- return data
134
-
135
-
136
- def _skip_unknown_chunk(fid: Reader, en: Endian) -> None:
137
- data = fid.read(4)
138
-
139
- if len(data) == 4:
140
- size = struct.unpack(f"{en}I", data)[0]
141
- if size == 0:
142
- raise WavError("Unknown chunk size is 0")
143
- fid.seek(size, 1)
144
- _handle_pad_byte(fid, size)
145
- elif len(data) == 0:
146
- pass # It's okay, we've hit EOF
147
- else:
148
- raise WavError(
149
- f"Unknown chunk size has wrong length, expected 4, got {len(data)}"
150
- )
151
-
152
-
153
- def _read_rf64_chunk(fid: Reader) -> tuple[int, int, Endian]:
154
- # https://tech.ebu.ch/docs/tech/tech3306v1_0.pdf
155
- # https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.2088-1-201910-I!!PDF-E.pdf
156
-
157
- heading = fid.read(12)
158
- if heading != b"\xff\xff\xff\xffWAVEds64":
159
- raise WavError(f"Invalid heading for rf64 chunk: {heading!r}")
160
-
161
- chunk_size = fid.read(4)
162
-
163
- bw_size_low = fid.read(4)
164
- bw_size_high = fid.read(4)
165
-
166
- en: Endian = ">" if (bw_size_high > bw_size_low) else "<"
167
- bytes_order: ByteOrd = "big" if en == ">" else "little"
168
-
169
- data_size_low = fid.read(4)
170
- data_size_high = fid.read(4)
171
-
172
- file_size = int.from_bytes(bw_size_low + bw_size_high, "little")
173
- data_size = int.from_bytes(data_size_low + data_size_high, "little")
174
-
175
- int_chunk_size = int.from_bytes(chunk_size, bytes_order)
176
- if type(int_chunk_size) is not int or int_chunk_size > 40:
177
- raise WavError("Invalid chunk size in RF64 chunk")
178
-
179
- fid.read(40 - int_chunk_size)
180
-
181
- return data_size, file_size, en
182
-
183
-
184
- def _read_riff_chunk(sig: bytes, fid: Reader) -> tuple[None, int, Endian]:
185
- en: Endian = "<" if sig == b"RIFF" else ">"
186
- bytes_order: ByteOrd = "big" if en == ">" else "little"
187
-
188
- file_size = int.from_bytes(fid.read(4), bytes_order) + 8
189
-
190
- form = fid.read(4)
191
- if form != b"WAVE":
192
- raise WavError(f"Not a WAV file. RIFF form type is {form!r}.")
193
-
194
- return None, file_size, en
195
-
196
-
197
- def _handle_pad_byte(fid: Reader, size: int) -> None:
198
- if size % 2 == 1:
199
- fid.seek(1, 1)
200
-
201
-
202
- def read(fid: Reader) -> tuple[int, AudioData]:
203
- file_sig = fid.read(4)
204
- if file_sig in {b"RIFF", b"RIFX"}:
205
- data_size, file_size, en = _read_riff_chunk(file_sig, fid)
206
- elif file_sig == b"RF64":
207
- data_size, file_size, en = _read_rf64_chunk(fid)
208
- else:
209
- raise WavError(f"File format {file_sig!r} not supported.")
210
-
211
- bytes_order: ByteOrd = "big" if en == ">" else "little"
212
- fmt_chunk_received = False
213
-
214
- while fid.tell() < file_size:
215
- chunk_id = fid.read(4)
216
-
217
- if not chunk_id:
218
- raise WavError("Unexpected end of file.")
219
- if len(chunk_id) < 4:
220
- raise WavError(f"Incomplete chunk ID: {chunk_id!r}")
221
-
222
- if chunk_id == b"fmt ":
223
- fmt_chunk_received = True
224
- format_tag, channels, sr, block_align, bit_depth = _read_fmt_chunk(
225
- fid, bytes_order
226
- )
227
- elif chunk_id == b"data":
228
- if not fmt_chunk_received:
229
- raise WavError("No fmt chunk before data")
230
-
231
- data = _read_data_chunk(
232
- fid,
233
- format_tag,
234
- channels,
235
- bit_depth,
236
- en,
237
- block_align,
238
- data_size,
239
- )
240
-
241
- fid.seek(0)
242
- return sr, data
243
-
244
- elif chunk_id == b"\x00\x00\x00\x00":
245
- raise WavError("Invalid chunk ID")
246
- else:
247
- _skip_unknown_chunk(fid, en)
248
-
249
- raise WavError("Found no data")
250
-
251
-
252
- def write(fid: Writer, sr: int, arr: np.ndarray) -> None:
253
- channels = 1 if arr.ndim == 1 else arr.shape[1]
254
- bit_depth = arr.dtype.itemsize * 8
255
- block_align = channels * (bit_depth // 8)
256
- data_size = arr.nbytes
257
- total_size = 44 + data_size # Basic WAV header size + data size
258
-
259
- if is_rf64 := total_size > 0xFFFFFFFF:
260
- fid.write(b"RF64\xff\xff\xff\xffWAVE")
261
- ds64_size = 28
262
- ds64_chunk_data = (0).to_bytes(ds64_size, "little") # placeholder values
263
- fid.write(b"ds64" + struct.pack("<I", ds64_size) + ds64_chunk_data)
264
- else:
265
- fid.write(b"RIFF" + struct.pack("<I", total_size - 8) + b"WAVE")
266
-
267
- dkind = arr.dtype.kind
268
- format_tag = IEEE_FLOAT if dkind == "f" else PCM
269
-
270
- fmt_chunk_data = struct.pack(
271
- "<HHIIHH", format_tag, channels, sr, 0, block_align, bit_depth
272
- )
273
- fid.write(b"fmt " + struct.pack("<I", len(fmt_chunk_data)) + fmt_chunk_data)
274
-
275
- # Data chunk
276
- fid.write(b"data")
277
- fid.write(struct.pack("<I", 0xFFFFFFFF if is_rf64 else data_size))
278
-
279
- if arr.dtype.byteorder == ">" or (
280
- arr.dtype.byteorder == "=" and sys.byteorder == "big"
281
- ):
282
- arr = arr.byteswap()
283
- fid.write(arr.ravel().view("b").data)
284
-
285
- if is_rf64:
286
- end_position = fid.tell()
287
- fid.seek(16) # Position at the start of 'ds64' chunk size
288
-
289
- file_size = end_position - 20
290
- fid.write(struct.pack("<I", ds64_size))
291
- fid.write(file_size.to_bytes(8, "little") + data_size.to_bytes(8, "little"))
292
-
293
- fid.seek(end_position)
294
-
295
-
296
- def main() -> None:
297
- data = np.random.rand(48000, 2)
298
- with open("test.wav", "wb") as file:
299
- write(file, 48_000, data)
300
-
301
- with open("test.wav", "rb") as file:
302
- read_sr, read_data = read(file)
303
-
304
- assert read_sr == 48_000
305
- assert np.array_equal(data, read_data)
306
- print("success")
307
-
308
-
309
- if __name__ == "__main__":
310
- main()