wavemap 2.0.1__tar.gz → 2.1.0__tar.gz

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,23 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: wavemap
3
- Version: 2.0.1
4
- Summary: Memory map WAVE or raw audio files
5
- Home-page: https://github.com/rec/wavemap
3
+ Version: 2.1.0
4
+ Summary: 🌊 Memory map WAVE or raw audio files 🌊
6
5
  Author: Tom Ritchford
7
- Author-email: tom@swirly.com
8
- License: MIT
9
- Keywords: testing,modules
10
- Platform: UNKNOWN
11
- Classifier: Development Status :: 4 - Beta
12
- Classifier: Programming Language :: Python :: 3.6
13
- Classifier: Programming Language :: Python :: 3.7
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
- Classifier: Intended Audience :: Developers
17
- Classifier: License :: OSI Approved :: MIT License
18
- Classifier: Topic :: Software Development :: Libraries
19
- Classifier: Topic :: Utilities
20
- License-File: LICENSE
6
+ Author-email: Tom Ritchford <tom@swirly.com>
7
+ License-Expression: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Requires-Dist: numpy>=1.24.1,<2
15
+ Requires-Dist: xmod>=1.3.2,<2
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/x-rst
21
18
 
22
19
  🌊 Memory map WAVE files into numpy arrays 🌊
23
20
  ----------------------------------------------
@@ -379,5 +376,3 @@ ARGUMENTS
379
376
  If true, ``arr`` is copied even if it is already the requested type
380
377
 
381
378
  (automatically generated by `doks <https://github.com/rec/doks/>`_ on 2021-02-23T14:37:02.652534)
382
-
383
-
@@ -0,0 +1,47 @@
1
+ [project]
2
+ name = "wavemap"
3
+ version = "2.1.0"
4
+ description = "🌊 Memory map WAVE or raw audio files 🌊"
5
+ authors = [{ name = "Tom Ritchford", email = "tom@swirly.com" }]
6
+ requires-python = ">=3.10"
7
+ readme = "README.rst"
8
+ license = "MIT"
9
+ classifiers = ["Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14"]
10
+ dependencies = [
11
+ "numpy>=1.24.1,<2",
12
+ "xmod>=1.3.2,<2",
13
+ ]
14
+
15
+ [tool.doks]
16
+ auto = true
17
+ source = 'wavemap/__init__.py'
18
+
19
+ [tool.ruff]
20
+ [dependency-groups]
21
+ dev = [
22
+ "coverage>=7.1.0,<8",
23
+ "pytest>=7.2.1,<8",
24
+ "pyupgrade>=3.21.2",
25
+ "ruff>=0.14.14",
26
+ "stroll>=1.1.0,<2",
27
+ "tdir>=1.4.1,<2",
28
+ "ty>=0.0.14",
29
+ ]
30
+
31
+ [tool.uv]
32
+
33
+ [tool.uv.build-backend]
34
+ module-root = ""
35
+
36
+ [build-system]
37
+ requires = ["uv_build>=0.9.0,<0.10.0"]
38
+ build-backend = "uv_build"
39
+ [tool.coverage.run]
40
+ branch = true
41
+ source = "wavemap"
42
+
43
+ [tool.coverage.report]
44
+ fail_under = "68"
45
+ skip_covered = true
46
+ exclude_lines = ["pragma: no cover", "if False:", "if __name__ == .__main__.:", "raise NotImplementedError"]
47
+
Binary file
@@ -26,31 +26,34 @@ Typical usage:
26
26
  wm /= 2
27
27
  # Each sample in the file is scaled by half.
28
28
  """
29
+
30
+ from typing import Optional, Union
31
+ from collections.abc import Callable
32
+
33
+ import numpy as np
34
+ import xmod
35
+
29
36
  from . import docs
30
37
  from .convert import convert
31
38
  from .raw import RawMap, warn
32
39
  from .read import ReadMap as ReadMap
33
40
  from .write import WriteMap as WriteMap
34
- from typing import Callable, Optional, Union
35
- import numpy as np
36
- import xmod
37
41
 
38
42
  __all__ = (
39
- 'wavemap',
40
- 'RawMap',
41
- 'ReadMap',
42
- 'WriteMap',
43
- 'copy_to',
44
- 'new_like',
45
- 'convert',
43
+ "wavemap",
44
+ "RawMap",
45
+ "ReadMap",
46
+ "WriteMap",
47
+ "copy_to",
48
+ "new_like",
49
+ "convert",
46
50
  )
47
- __version__ = '2.0.1'
48
51
 
49
52
  copy_to = WriteMap.copy_to
50
53
  new_like = WriteMap.new_like
51
- _DOKS = {warn: '<function warn: print to stderr>'}
52
- _WRITE_PARAMETERS = 'dtype', 'shape', 'sample_rate'
53
- _READ_PARAMETERS = 'order', 'always_2d'
54
+ _DOKS = {warn: "<function warn: print to stderr>"}
55
+ _WRITE_PARAMETERS = "dtype", "shape", "sample_rate"
56
+ _READ_PARAMETERS = "order", "always_2d"
54
57
 
55
58
 
56
59
  @xmod(mutable=True)
@@ -60,20 +63,20 @@ def wavemap(
60
63
  #
61
64
  # Read parameters
62
65
  #
63
- mode: str = 'r',
64
- order: Optional[str] = None,
66
+ mode: str = "r",
67
+ order: str | None = None,
65
68
  always_2d: bool = False,
66
69
  #
67
70
  # Write parameters
68
71
  #
69
- dtype: Optional[np.dtype] = None,
70
- shape: Union[None, int, tuple] = None,
72
+ dtype: np.dtype | None = None,
73
+ shape: None | int | tuple = None,
71
74
  sample_rate: int = 0,
72
75
  roffset: int = 0,
73
76
  #
74
77
  # Read and write parameters
75
78
  #
76
- warn: Optional[Callable] = warn,
79
+ warn: Callable | None = warn,
77
80
  ):
78
81
  """
79
82
  Memory map a WAVE file to a `numpy` array
@@ -81,18 +84,18 @@ def wavemap(
81
84
  Return an instance of `ReadMap` or `WriteMap`, depending on
82
85
  `mode`.
83
86
  """
84
- if mode.startswith('w'):
87
+ if mode.startswith("w"):
85
88
  if not dtype:
86
- raise ValueError('dtype must be set for write')
89
+ raise ValueError("dtype must be set for write")
87
90
  if not shape:
88
- raise ValueError('shape must be set for write')
91
+ raise ValueError("shape must be set for write")
89
92
  if not sample_rate:
90
- raise ValueError('sample_rate must be set for write')
93
+ raise ValueError("sample_rate must be set for write")
91
94
 
92
95
  if order:
93
- raise ValueError('order cannot be set for write')
96
+ raise ValueError("order cannot be set for write")
94
97
  if always_2d:
95
- raise ValueError('always_2d cannot be set for write')
98
+ raise ValueError("always_2d cannot be set for write")
96
99
 
97
100
  return WriteMap(
98
101
  filename=filename,
@@ -104,9 +107,9 @@ def wavemap(
104
107
  )
105
108
  else:
106
109
  if shape:
107
- raise ValueError('shape cannot be set for write')
110
+ raise ValueError("shape cannot be set for write")
108
111
  if sample_rate:
109
- raise ValueError('sample_rate cannot be set for write')
112
+ raise ValueError("sample_rate cannot be set for write")
110
113
 
111
114
  result = ReadMap(
112
115
  filename=filename,
@@ -1,11 +1,10 @@
1
- from numpy.lib.stride_tricks import as_strided
2
1
  from typing import Optional
2
+
3
3
  import numpy as np
4
+ from numpy.lib.stride_tricks import as_strided
4
5
 
5
6
 
6
- def convert(
7
- arr: np.ndarray, dtype: Optional[np.dtype], must_copy: bool = False
8
- ):
7
+ def convert(arr: np.ndarray, dtype: np.dtype | None, must_copy: bool = False):
9
8
  """
10
9
  Returns a copy of a numpy array or matrix that represents audio data in
11
10
  another type, scaling and shifting as necessary.
@@ -27,8 +26,8 @@ def convert(
27
26
  arr = np.copy(arr)
28
27
  return arr
29
28
 
30
- old_int = 'int' in str(old_t)
31
- new_int = 'int' in str(new_t)
29
+ old_int = "int" in str(old_t)
30
+ new_int = "int" in str(new_t)
32
31
 
33
32
  if not (new_int or old_int):
34
33
  # Convert between floats
@@ -96,8 +95,15 @@ def _twenty_four_bit(shape, new, raw, itemsize):
96
95
  assert not (frames % 4)
97
96
 
98
97
  # https://stackoverflow.com/a/34128171/4383
99
- raw = new(shape=(itemsize * frames // 4,), dtype='int32')
100
- strided = as_strided(raw, strides=(12, 3,), shape=(frames, 4))
98
+ raw = new(shape=(itemsize * frames // 4,), dtype="int32")
99
+ strided = as_strided(
100
+ raw,
101
+ strides=(
102
+ 12,
103
+ 3,
104
+ ),
105
+ shape=(frames, 4),
106
+ )
101
107
  reshaped = np.reshape(strided, shape=shape)
102
108
 
103
109
  result = reshaped & 0x00FFFFFF
@@ -15,9 +15,9 @@ Think of this as `self`. (This is because you need to implement `__new__`
15
15
  and not `__init__` when deriving from `np.darray`.)
16
16
  """
17
17
 
18
- DTYPE = 'The numpy datatype of the samples in the file.'
18
+ DTYPE = "The numpy datatype of the samples in the file."
19
19
 
20
- FILENAME = 'The name of the file being mapped'
20
+ FILENAME = "The name of the file being mapped"
21
21
 
22
22
  ORDER = """
23
23
  Samples usually get laid out in into a `numpy.darray` with`
@@ -63,9 +63,9 @@ In mode `'w+'`, "write", the file is opened for write, and overwrites
63
63
  whatever else is there.
64
64
  """
65
65
 
66
- OFFSET = 'How many bytes in the file before the WAV data'
67
- ROFFSET = 'How many bytes in the file after the WAV data'
68
- SAMPLE_RATE = 'The sample rate in Hz (cycles per second)'
66
+ OFFSET = "How many bytes in the file before the WAV data"
67
+ ROFFSET = "How many bytes in the file after the WAV data"
68
+ SAMPLE_RATE = "The sample rate in Hz (cycles per second)"
69
69
 
70
70
  SHAPE = """
71
71
  The shape of the resulting numpy.darray. Can be a tuple, or a positive
@@ -87,19 +87,19 @@ def arguments(*names, subs=None):
87
87
  names = [(i, subs.get(i, i).upper()) for i in names]
88
88
  missing = [n for (n, a) in names if a not in globals()]
89
89
  if missing:
90
- raise ValueError(f'Cannot document arguments {missing}')
90
+ raise ValueError(f"Cannot document arguments {missing}")
91
91
 
92
- yield 'ARGUMENTS'
92
+ yield "ARGUMENTS"
93
93
  for name, attr in names:
94
- yield f' {name}'
94
+ yield f" {name}"
95
95
  for line in globals()[attr].strip().splitlines():
96
- yield line and f' {line}'
97
- yield ''
96
+ yield line and f" {line}"
97
+ yield ""
98
98
 
99
99
 
100
100
  def add_arguments(func, names, subs=None):
101
101
  params = arguments(*names, subs=subs)
102
- func.__doc__ = func.__doc__.rstrip() + '\n\n' + '\n'.join(params)
102
+ func.__doc__ = func.__doc__.rstrip() + "\n\n" + "\n".join(params)
103
103
  return func
104
104
 
105
105
 
@@ -1,9 +1,9 @@
1
- from numpy.compat import os_fspath, contextlib_nullcontext, is_pathlib_path
2
- from numpy.core.numeric import uint8, ndarray, dtype
3
- from numpy.core.overrides import set_module
4
1
  import numpy as np
2
+ from numpy.compat import contextlib_nullcontext, is_pathlib_path, os_fspath
3
+ from numpy.core.numeric import dtype, ndarray, uint8
4
+ from numpy.core.overrides import set_module
5
5
 
6
- __all__ = ['memmap']
6
+ __all__ = ["memmap"]
7
7
 
8
8
  dtypedescr = dtype
9
9
  valid_filemodes = ["r", "c", "r+", "w+"]
@@ -17,7 +17,7 @@ mode_equivalents = {
17
17
  }
18
18
 
19
19
 
20
- @set_module('numpy')
20
+ @set_module("numpy")
21
21
  class memmap(ndarray):
22
22
  """Create a memory-map to an array stored in a *binary* file on disk.
23
23
 
@@ -204,10 +204,10 @@ class memmap(ndarray):
204
204
  subtype,
205
205
  filename,
206
206
  dtype=uint8,
207
- mode='r+',
207
+ mode="r+",
208
208
  offset=0,
209
209
  shape=None,
210
- order='C',
210
+ order="C",
211
211
  roffset=0,
212
212
  ):
213
213
  # Import here to minimize 'import numpy' overhead
@@ -224,15 +224,13 @@ class memmap(ndarray):
224
224
  )
225
225
  ) from None
226
226
 
227
- if mode == 'w+' and shape is None:
227
+ if mode == "w+" and shape is None:
228
228
  raise ValueError("shape must be given")
229
229
 
230
- if hasattr(filename, 'read'):
230
+ if hasattr(filename, "read"):
231
231
  f_ctx = contextlib_nullcontext(filename)
232
232
  else:
233
- f_ctx = open(
234
- os_fspath(filename), ('r' if mode == 'c' else mode) + 'b'
235
- )
233
+ f_ctx = open(os_fspath(filename), ("r" if mode == "c" else mode) + "b")
236
234
 
237
235
  with f_ctx as fid:
238
236
  fid.seek(0, 2)
@@ -260,14 +258,14 @@ class memmap(ndarray):
260
258
 
261
259
  bytes = int(offset + size * _dbytes + roffset)
262
260
 
263
- if mode in ('w+', 'r+') and flen < bytes:
261
+ if mode in ("w+", "r+") and flen < bytes:
264
262
  fid.seek(bytes - 1, 0)
265
- fid.write(b'\0')
263
+ fid.write(b"\0")
266
264
  fid.flush()
267
265
 
268
- if mode == 'c':
266
+ if mode == "c":
269
267
  acc = mmap.ACCESS_COPY
270
- elif mode == 'r':
268
+ elif mode == "r":
271
269
  acc = mmap.ACCESS_READ
272
270
  else:
273
271
  acc = mmap.ACCESS_WRITE
@@ -304,7 +302,7 @@ class memmap(ndarray):
304
302
  return self
305
303
 
306
304
  def __array_finalize__(self, obj):
307
- if hasattr(obj, '_mmap') and np.may_share_memory(self, obj):
305
+ if hasattr(obj, "_mmap") and np.may_share_memory(self, obj):
308
306
  self._mmap = obj._mmap
309
307
  self.filename = obj.filename
310
308
  self.offset = obj.offset
@@ -330,11 +328,11 @@ class memmap(ndarray):
330
328
  memmap
331
329
 
332
330
  """
333
- if self.base is not None and hasattr(self.base, 'flush'):
331
+ if self.base is not None and hasattr(self.base, "flush"):
334
332
  self.base.flush()
335
333
 
336
334
  def __array_wrap__(self, arr, context=None):
337
- arr = super(memmap, self).__array_wrap__(arr, context)
335
+ arr = super().__array_wrap__(arr, context)
338
336
 
339
337
  # Return a memmap if a memmap was given as the output of the
340
338
  # ufunc. Leave the arr class unchanged if self is not a memmap
@@ -349,7 +347,7 @@ class memmap(ndarray):
349
347
  return arr.view(np.ndarray)
350
348
 
351
349
  def __getitem__(self, index):
352
- res = super(memmap, self).__getitem__(index)
350
+ res = super().__getitem__(index)
353
351
  if type(res) is memmap and res._mmap is None:
354
352
  return res.view(type=ndarray)
355
353
  return res
@@ -1,10 +1,13 @@
1
+ import sys
2
+ from typing import Optional, Union
3
+ from collections.abc import Callable
4
+
5
+ import numpy as np
6
+
1
7
  from . import docs
2
8
  from .memmap import memmap
3
- from typing import Callable, Optional, Union
4
- import numpy as np
5
- import sys
6
9
 
7
- int24 = 'int24'
10
+ int24 = "int24"
8
11
 
9
12
 
10
13
  def warn(msg):
@@ -12,20 +15,20 @@ def warn(msg):
12
15
 
13
16
 
14
17
  class RawMap(memmap):
15
- """"Memory map raw audio data from a disk file into a numpy matrix"""
18
+ """ "Memory map raw audio data from a disk file into a numpy matrix"""
16
19
 
17
20
  @docs.update
18
21
  def __new__(
19
22
  cls,
20
23
  filename: str,
21
24
  dtype: np.dtype,
22
- shape: Union[tuple, int, None] = None,
23
- mode: str = 'r',
25
+ shape: tuple | int | None = None,
26
+ mode: str = "r",
24
27
  offset: int = 0,
25
28
  roffset: int = 0,
26
- order: Optional[str] = None,
29
+ order: str | None = None,
27
30
  always_2d: bool = False,
28
- warn: Optional[Callable] = warn,
31
+ warn: Callable | None = warn,
29
32
  ):
30
33
  """Memory map raw audio data from a disk file into a numpy matrix"""
31
34
  # Documentation for parameters is in docs.py
@@ -36,22 +39,22 @@ class RawMap(memmap):
36
39
  )
37
40
 
38
41
  if offset < 0 or roffset < 0:
39
- raise ValueError('offset and roffset must be non-negative')
42
+ raise ValueError("offset and roffset must be non-negative")
40
43
 
41
- if order not in ('C', 'F', None):
44
+ if order not in ("C", "F", None):
42
45
  raise ValueError(f'Bad order "{order}"')
43
46
 
44
47
  if isinstance(shape, int):
45
48
  shape = (shape,)
46
49
 
47
50
  if not (shape is None or 1 <= len(shape) <= 2):
48
- raise ValueError('Wave files must have 1 or 2 dimensions')
51
+ raise ValueError("Wave files must have 1 or 2 dimensions")
49
52
 
50
- if 'w' in mode:
53
+ if "w" in mode:
51
54
  if not shape:
52
- raise ValueError('Must set a shape in write mode')
53
- order = order or 'FC'[max(shape) == shape[0]]
54
- return new(mode='w+', order=order)
55
+ raise ValueError("Must set a shape in write mode")
56
+ order = order or "FC"[max(shape) == shape[0]]
57
+ return new(mode="w+", order=order)
55
58
 
56
59
  if str(dtype) == int24:
57
60
  itemsize = 3
@@ -62,12 +65,12 @@ class RawMap(memmap):
62
65
  audio_size = file_size - offset - roffset
63
66
  shape = _get_shape(shape, audio_size, itemsize, order, always_2d, warn)
64
67
  if itemsize == 3:
65
- raise ValueError('Cannot memory map 24-bit audio')
68
+ raise ValueError("Cannot memory map 24-bit audio")
66
69
  return new(shape=shape)
67
70
 
68
71
 
69
72
  def file_byte_size(filename: str):
70
- with open(filename, 'rb') as fp:
73
+ with open(filename, "rb") as fp:
71
74
  return fp.seek(0, 2)
72
75
 
73
76
 
@@ -78,27 +81,27 @@ def _get_shape(shape, audio_size, itemsize, order, always_2d, warn):
78
81
  if itemsize == 3:
79
82
  extra = audio_size % 12
80
83
  if extra and warn:
81
- s = 's' if len(extra) != 1 else ''
82
- warn(f'24-bit conversion lost last {extra} byte{s}, sorry')
84
+ s = "s" if len(extra) != 1 else ""
85
+ warn(f"24-bit conversion lost last {extra} byte{s}, sorry")
83
86
  audio_size -= extra
84
87
 
85
88
  frame_size = itemsize * channels
86
89
  frames = audio_size // frame_size
87
90
 
88
91
  if frames_requested and frames_requested < frames:
89
- warn(f'Requested {frames_requested} frames, got {frames}')
92
+ warn(f"Requested {frames_requested} frames, got {frames}")
90
93
  frames = frames_requested
91
94
 
92
95
  if warn:
93
96
  extra = audio_size % frame_size
94
97
  if extra:
95
- s = '' if extra == 1 else 's'
96
- warn(f'{extra} byte{s} after end-of-frame discarded')
98
+ s = "" if extra == 1 else "s"
99
+ warn(f"{extra} byte{s} after end-of-frame discarded")
97
100
 
98
101
  if channels == 1 and not always_2d:
99
102
  return (frames,)
100
103
 
101
- if not order or order == 'C':
104
+ if not order or order == "C":
102
105
  return frames, channels
103
106
 
104
107
  return channels, frames