Skip to content

Commit 7deb23a

Browse files
authored
Merge pull request #326 from bastibe/bastibe/0.11.0
Release 0.11.0
2 parents 8737cfb + c426f56 commit 7deb23a

File tree

6 files changed

+100
-72
lines changed

6 files changed

+100
-72
lines changed

README.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,20 @@ News
292292
- Improves performance of `blocks()` and `SoundFile.blocks()`.
293293
- Improves import time by using CFFI's out of line mode.
294294
- Adds a build script for building distributions.
295+
296+
2022-06-02 V0.11.0 Bastian Bechtold:
297+
Thank you, tennies, Hannes Helmholz, Christoph Boeddeker, Matt
298+
Vollrath, Matthias Geier, Jacek Konieczny, Boris Verkhovskiy,
299+
Jonas Haag, Eduardo Moguillansky, Panos Laganakos, Jarvy Jarvison,
300+
Domingo Ramirez, Tim Chagnon, Kyle Benesch, Fabian-Robert Stöter,
301+
Joe Todd
302+
303+
- MP3 support
304+
- Adds binary wheels for macOS M1
305+
- Improves compatibility with macOS, specifically for M1 machines
306+
- Fixes file descriptor open for binary wheels on Windows and Python 3.5+
307+
- Updates libsndfile to v1.1.0
308+
- Adds get_strings method for retrieving all metadata at once
309+
- Improves documentation, error messages and tests
310+
- Displays length of very short files in samples
311+
- Supports the file system path protocol (pathlib et al)

build_wheels.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
import os
22
import shutil
33

4-
architectures = dict(darwin=['64bit'],
4+
architectures = dict(darwin=['x86_64', 'arm64'],
55
win32=['32bit', '64bit'],
66
noplatform='noarch')
77

88
def cleanup():
9+
os.environ['PYSOUNDFILE_PLATFORM'] = ''
10+
os.environ['PYSOUNDFILE_ARCHITECTURE'] = ''
911
shutil.rmtree('build', ignore_errors=True)
12+
shutil.rmtree('soundfile.egg-info', ignore_errors=True)
1013
try:
1114
os.remove('_soundfile.py')
1215
except:
1316
pass
1417

1518
for platform, archs in architectures.items():
16-
os.environ['PYSOUNDFILE_PLATFORM'] = platform
1719
for arch in archs:
20+
os.environ['PYSOUNDFILE_PLATFORM'] = platform
1821
os.environ['PYSOUNDFILE_ARCHITECTURE'] = arch
22+
os.system('python3 setup.py bdist_wheel')
1923
cleanup()
20-
os.system('python setup.py bdist_wheel')
2124

25+
os.system('python3 setup.py sdist')
2226
cleanup()
23-
os.system('python setup.py sdist')

setup.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,14 @@
55
from setuptools.command.test import test as TestCommand
66
import sys
77

8-
PYTHON_INTERPRETERS = '.'.join([
9-
'cp26', 'cp27',
10-
'cp32', 'cp33', 'cp34', 'cp35', 'cp36',
11-
'pp27',
12-
'pp32', 'pp33',
13-
])
14-
MACOSX_VERSIONS = '.'.join([
15-
'macosx_10_5_x86_64',
16-
'macosx_10_6_intel',
17-
'macosx_10_9_intel',
18-
'macosx_10_9_x86_64',
19-
])
20-
218
# environment variables for cross-platform package creation
229
platform = os.environ.get('PYSOUNDFILE_PLATFORM', sys.platform)
2310
architecture0 = os.environ.get('PYSOUNDFILE_ARCHITECTURE', architecture()[0])
2411

2512
if platform == 'darwin':
26-
libname = 'libsndfile.dylib'
13+
libname = 'libsndfile_' + architecture0 + '.dylib'
2714
elif platform == 'win32':
28-
libname = 'libsndfile' + architecture0 + '.dll'
15+
libname = 'libsndfile_' + architecture0 + '.dll'
2916
else:
3017
libname = None
3118

@@ -70,9 +57,12 @@ class bdist_wheel_half_pure(bdist_wheel):
7057
"""Create OS-dependent, but Python-independent wheels."""
7158

7259
def get_tag(self):
73-
pythons = 'py2.py3.' + PYTHON_INTERPRETERS
60+
pythons = 'py2.py3'
7461
if platform == 'darwin':
75-
oses = MACOSX_VERSIONS
62+
if architecture0 == 'x86_64':
63+
oses = 'macosx_10_9_x86_64.macosx_11_0_x86_64'
64+
else:
65+
oses = 'macosx_10_9_arm64.macosx_11_0_arm64'
7666
elif platform == 'win32':
7767
if architecture0 == '32bit':
7868
oses = 'win32'
@@ -87,7 +77,7 @@ def get_tag(self):
8777

8878
setup(
8979
name='soundfile',
90-
version='0.10.3post1',
80+
version='0.11.0',
9181
description='An audio library based on libsndfile, CFFI and NumPy',
9282
author='Bastian Bechtold',
9383
author_email='[email protected]',

soundfile.py

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@
88
For further information, see https://python-soundfile.readthedocs.io/.
99
1010
"""
11-
__version__ = "0.10.3"
11+
__version__ = "0.11.0"
1212

1313
import os as _os
1414
import sys as _sys
15-
from platform import machine as _machine
1615
from os import SEEK_SET, SEEK_CUR, SEEK_END
1716
from ctypes.util import find_library as _find_library
1817
from _soundfile import ffi as _ffi
@@ -62,36 +61,44 @@
6261
'OGG': 0x200000, # Xiph OGG container
6362
'MPC2K': 0x210000, # Akai MPC 2000 sampler
6463
'RF64': 0x220000, # RF64 WAV file
64+
'MP3': 0x230000, # MPEG-1/2 audio stream
6565
}
6666

6767
_subtypes = {
68-
'PCM_S8': 0x0001, # Signed 8 bit data
69-
'PCM_16': 0x0002, # Signed 16 bit data
70-
'PCM_24': 0x0003, # Signed 24 bit data
71-
'PCM_32': 0x0004, # Signed 32 bit data
72-
'PCM_U8': 0x0005, # Unsigned 8 bit data (WAV and RAW only)
73-
'FLOAT': 0x0006, # 32 bit float data
74-
'DOUBLE': 0x0007, # 64 bit float data
75-
'ULAW': 0x0010, # U-Law encoded.
76-
'ALAW': 0x0011, # A-Law encoded.
77-
'IMA_ADPCM': 0x0012, # IMA ADPCM.
78-
'MS_ADPCM': 0x0013, # Microsoft ADPCM.
79-
'GSM610': 0x0020, # GSM 6.10 encoding.
80-
'VOX_ADPCM': 0x0021, # OKI / Dialogix ADPCM
81-
'G721_32': 0x0030, # 32kbs G721 ADPCM encoding.
82-
'G723_24': 0x0031, # 24kbs G723 ADPCM encoding.
83-
'G723_40': 0x0032, # 40kbs G723 ADPCM encoding.
84-
'DWVW_12': 0x0040, # 12 bit Delta Width Variable Word encoding.
85-
'DWVW_16': 0x0041, # 16 bit Delta Width Variable Word encoding.
86-
'DWVW_24': 0x0042, # 24 bit Delta Width Variable Word encoding.
87-
'DWVW_N': 0x0043, # N bit Delta Width Variable Word encoding.
88-
'DPCM_8': 0x0050, # 8 bit differential PCM (XI only)
89-
'DPCM_16': 0x0051, # 16 bit differential PCM (XI only)
90-
'VORBIS': 0x0060, # Xiph Vorbis encoding.
91-
'ALAC_16': 0x0070, # Apple Lossless Audio Codec (16 bit).
92-
'ALAC_20': 0x0071, # Apple Lossless Audio Codec (20 bit).
93-
'ALAC_24': 0x0072, # Apple Lossless Audio Codec (24 bit).
94-
'ALAC_32': 0x0073, # Apple Lossless Audio Codec (32 bit).
68+
'PCM_S8': 0x0001, # Signed 8 bit data
69+
'PCM_16': 0x0002, # Signed 16 bit data
70+
'PCM_24': 0x0003, # Signed 24 bit data
71+
'PCM_32': 0x0004, # Signed 32 bit data
72+
'PCM_U8': 0x0005, # Unsigned 8 bit data (WAV and RAW only)
73+
'FLOAT': 0x0006, # 32 bit float data
74+
'DOUBLE': 0x0007, # 64 bit float data
75+
'ULAW': 0x0010, # U-Law encoded.
76+
'ALAW': 0x0011, # A-Law encoded.
77+
'IMA_ADPCM': 0x0012, # IMA ADPCM.
78+
'MS_ADPCM': 0x0013, # Microsoft ADPCM.
79+
'GSM610': 0x0020, # GSM 6.10 encoding.
80+
'VOX_ADPCM': 0x0021, # OKI / Dialogix ADPCM
81+
'NMS_ADPCM_16': 0x0022, # 16kbs NMS G721-variant encoding.
82+
'NMS_ADPCM_24': 0x0023, # 24kbs NMS G721-variant encoding.
83+
'NMS_ADPCM_32': 0x0024, # 32kbs NMS G721-variant encoding.
84+
'G721_32': 0x0030, # 32kbs G721 ADPCM encoding.
85+
'G723_24': 0x0031, # 24kbs G723 ADPCM encoding.
86+
'G723_40': 0x0032, # 40kbs G723 ADPCM encoding.
87+
'DWVW_12': 0x0040, # 12 bit Delta Width Variable Word encoding.
88+
'DWVW_16': 0x0041, # 16 bit Delta Width Variable Word encoding.
89+
'DWVW_24': 0x0042, # 24 bit Delta Width Variable Word encoding.
90+
'DWVW_N': 0x0043, # N bit Delta Width Variable Word encoding.
91+
'DPCM_8': 0x0050, # 8 bit differential PCM (XI only)
92+
'DPCM_16': 0x0051, # 16 bit differential PCM (XI only)
93+
'VORBIS': 0x0060, # Xiph Vorbis encoding.
94+
'OPUS': 0x0064, # Xiph/Skype Opus encoding.
95+
'ALAC_16': 0x0070, # Apple Lossless Audio Codec (16 bit).
96+
'ALAC_20': 0x0071, # Apple Lossless Audio Codec (20 bit).
97+
'ALAC_24': 0x0072, # Apple Lossless Audio Codec (24 bit).
98+
'ALAC_32': 0x0073, # Apple Lossless Audio Codec (32 bit).
99+
'MPEG_LAYER_I': 0x0080, # MPEG-1 Audio Layer I.
100+
'MPEG_LAYER_II': 0x0081, # MPEG-1 Audio Layer II.
101+
'MPEG_LAYER_III': 0x0082, # MPEG-2 Audio Layer III.
95102
}
96103

97104
_endians = {
@@ -128,6 +135,7 @@
128135
'OGG': 'VORBIS',
129136
'MPC2K': 'PCM_16',
130137
'RF64': 'PCM_16',
138+
'MP3': 'MPEG_LAYER_III',
131139
}
132140

133141
_ffi_types = {
@@ -144,10 +152,16 @@
144152
_snd = _ffi.dlopen(_libname)
145153
except OSError:
146154
if _sys.platform == 'darwin':
155+
from platform import machine as _machine
156+
_packaged_libname = 'libsndfile_' + _machine() + '.dylib'
147157
_libname = 'libsndfile.dylib'
148158
elif _sys.platform == 'win32':
149159
from platform import architecture as _architecture
150-
_libname = 'libsndfile' + _architecture()[0] + '.dll'
160+
_packaged_libname = 'libsndfile_' + _architecture()[0] + '.dll'
161+
_libname = 'libsndfile.dll'
162+
elif _sys.platform == 'linux':
163+
_packaged_libname = 'libsndfile.so' # not provided!
164+
_libname = 'libsndfile.so'
151165
else:
152166
raise
153167

@@ -160,16 +174,19 @@
160174
while not _os.path.isdir(_path):
161175
_path = _os.path.abspath(_os.path.join(_path, '..'))
162176

163-
# Homebrew on Apple M1 uses a `/opt/homebrew/lib` instead of
164-
# `/usr/local/lib`. We are making sure we pick that up.
165-
if _sys.platform == 'darwin' and _machine() == 'arm64':
166-
_hbrew_path = '/opt/homebrew/lib/' if _os.path.isdir('/opt/homebrew/lib/') \
167-
else '/usr/local/lib/'
168-
_snd = _ffi.dlopen(_os.path.join(
169-
_hbrew_path, _libname))
170-
else:
171-
_snd = _ffi.dlopen(_os.path.join(
172-
_path, '_soundfile_data', _libname))
177+
try: # packaged libsndfile:
178+
_snd = _ffi.dlopen(_os.path.join(_path, '_soundfile_data', _packaged_libname))
179+
except OSError: # try system-wide libsndfile:
180+
# Homebrew on Apple M1 uses a `/opt/homebrew/lib` instead of
181+
# `/usr/local/lib`. We are making sure we pick that up.
182+
from platform import machine as _machine
183+
if _sys.platform == 'darwin' and _machine() == 'arm64':
184+
_hbrew_path = '/opt/homebrew/lib/' if _os.path.isdir('/opt/homebrew/lib/') \
185+
else '/usr/local/lib/'
186+
_snd = _ffi.dlopen(_os.path.join(_hbrew_path, _libname))
187+
else:
188+
# Try explicit file name, if the general does not work (e.g. on nixos)
189+
_snd = _ffi.dlopen(_libname)
173190

174191
__libsndfile_version__ = _ffi.string(_snd.sf_version_string()).decode('utf-8', 'replace')
175192
if __libsndfile_version__.startswith('libsndfile-'):
@@ -1368,9 +1385,9 @@ def copy_metadata(self):
13681385
-------
13691386
13701387
metadata: dict[str, str]
1371-
A dict with all metadata. Possible keys are: 'title', 'copyright',
1372-
'software', 'artist', 'comment', 'date', 'album', 'license',
1373-
'tracknumber' and 'genre'.
1388+
A dict with all metadata. Possible keys are: 'title', 'copyright',
1389+
'software', 'artist', 'comment', 'date', 'album', 'license',
1390+
'tracknumber' and 'genre'.
13741391
"""
13751392
strs = {}
13761393
for strtype, strid in _str_types.items():

tests/test_soundfile.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,31 +101,31 @@ def file_wplus(request):
101101
return _file_new(request, os.O_CREAT | os.O_RDWR, 'w+b')
102102

103103

104-
@pytest.yield_fixture
104+
@pytest.fixture
105105
def file_inmemory():
106106
with io.BytesIO() as f:
107107
yield f
108108

109109

110-
@pytest.yield_fixture
110+
@pytest.fixture
111111
def sf_stereo_r(file_stereo_r):
112112
with sf.SoundFile(file_stereo_r) as f:
113113
yield f
114114

115115

116-
@pytest.yield_fixture
116+
@pytest.fixture
117117
def sf_stereo_w(file_w):
118118
with sf.SoundFile(file_w, 'w', 44100, 2, format='WAV') as f:
119119
yield f
120120

121121

122-
@pytest.yield_fixture
122+
@pytest.fixture
123123
def sf_stereo_rplus(file_stereo_rplus):
124124
with sf.SoundFile(file_stereo_rplus, 'r+') as f:
125125
yield f
126126

127127

128-
@pytest.yield_fixture
128+
@pytest.fixture
129129
def sf_stereo_wplus(file_wplus):
130130
with sf.SoundFile(file_wplus, 'w+', 44100, 2,
131131
format='WAV', subtype='FLOAT') as f:
@@ -592,7 +592,8 @@ def test_file_content(sf_stereo_r):
592592

593593
def test_file_attributes_in_read_mode(sf_stereo_r):
594594
if isinstance(sf_stereo_r.name, str):
595-
assert sf_stereo_r.name == filename_stereo
595+
# wrap in pathlib, to make tests pass on Windows:
596+
assert pathlib.Path(sf_stereo_r.name) == pathlib.Path(filename_stereo)
596597
elif not isinstance(sf_stereo_r.name, int):
597598
assert sf_stereo_r.name.name == filename_stereo
598599
assert sf_stereo_r.mode == 'r'

0 commit comments

Comments
 (0)