Prompt48 commited on
Commit
90c220d
·
verified ·
1 Parent(s): 9bf1160

Upload edit\Qwen3-TTS-test\.venv\Lib\site-packages\soundfile.py with huggingface_hub

Browse files
edit//Qwen3-TTS-test//.venv//Lib//site-packages//soundfile.py ADDED
@@ -0,0 +1,1674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """python-soundfile is an audio library based on libsndfile, CFFI and NumPy.
2
+
3
+ Sound files can be read or written directly using the functions
4
+ `read()` and `write()`.
5
+ To read a sound file in a block-wise fashion, use `blocks()`.
6
+ Alternatively, sound files can be opened as `SoundFile` objects.
7
+
8
+ For further information, see https://python-soundfile.readthedocs.io/.
9
+
10
+ """
11
+ __version__ = "0.13.1"
12
+
13
+ import os as _os
14
+ import sys as _sys
15
+ from os import SEEK_SET, SEEK_CUR, SEEK_END
16
+ from ctypes.util import find_library as _find_library
17
+ from _soundfile import ffi as _ffi
18
+
19
+ try:
20
+ _unicode = unicode # doesn't exist in Python 3.x
21
+ except NameError:
22
+ _unicode = str
23
+
24
+
25
+ _str_types = {
26
+ 'title': 0x01,
27
+ 'copyright': 0x02,
28
+ 'software': 0x03,
29
+ 'artist': 0x04,
30
+ 'comment': 0x05,
31
+ 'date': 0x06,
32
+ 'album': 0x07,
33
+ 'license': 0x08,
34
+ 'tracknumber': 0x09,
35
+ 'genre': 0x10,
36
+ }
37
+
38
+ _formats = {
39
+ 'WAV': 0x010000, # Microsoft WAV format (little endian default).
40
+ 'AIFF': 0x020000, # Apple/SGI AIFF format (big endian).
41
+ 'AU': 0x030000, # Sun/NeXT AU format (big endian).
42
+ 'RAW': 0x040000, # RAW PCM data.
43
+ 'PAF': 0x050000, # Ensoniq PARIS file format.
44
+ 'SVX': 0x060000, # Amiga IFF / SVX8 / SV16 format.
45
+ 'NIST': 0x070000, # Sphere NIST format.
46
+ 'VOC': 0x080000, # VOC files.
47
+ 'IRCAM': 0x0A0000, # Berkeley/IRCAM/CARL
48
+ 'W64': 0x0B0000, # Sonic Foundry's 64 bit RIFF/WAV
49
+ 'MAT4': 0x0C0000, # Matlab (tm) V4.2 / GNU Octave 2.0
50
+ 'MAT5': 0x0D0000, # Matlab (tm) V5.0 / GNU Octave 2.1
51
+ 'PVF': 0x0E0000, # Portable Voice Format
52
+ 'XI': 0x0F0000, # Fasttracker 2 Extended Instrument
53
+ 'HTK': 0x100000, # HMM Tool Kit format
54
+ 'SDS': 0x110000, # Midi Sample Dump Standard
55
+ 'AVR': 0x120000, # Audio Visual Research
56
+ 'WAVEX': 0x130000, # MS WAVE with WAVEFORMATEX
57
+ 'SD2': 0x160000, # Sound Designer 2
58
+ 'FLAC': 0x170000, # FLAC lossless file format
59
+ 'CAF': 0x180000, # Core Audio File format
60
+ 'WVE': 0x190000, # Psion WVE format
61
+ 'OGG': 0x200000, # Xiph OGG container
62
+ 'MPC2K': 0x210000, # Akai MPC 2000 sampler
63
+ 'RF64': 0x220000, # RF64 WAV file
64
+ 'MP3': 0x230000, # MPEG-1/2 audio stream
65
+ }
66
+
67
+ _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
+ '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.
102
+ }
103
+
104
+ _endians = {
105
+ 'FILE': 0x00000000, # Default file endian-ness.
106
+ 'LITTLE': 0x10000000, # Force little endian-ness.
107
+ 'BIG': 0x20000000, # Force big endian-ness.
108
+ 'CPU': 0x30000000, # Force CPU endian-ness.
109
+ }
110
+
111
+ # libsndfile doesn't specify default subtypes, these are somehow arbitrary:
112
+ _default_subtypes = {
113
+ 'WAV': 'PCM_16',
114
+ 'AIFF': 'PCM_16',
115
+ 'AU': 'PCM_16',
116
+ # 'RAW': # subtype must be explicit!
117
+ 'PAF': 'PCM_16',
118
+ 'SVX': 'PCM_16',
119
+ 'NIST': 'PCM_16',
120
+ 'VOC': 'PCM_16',
121
+ 'IRCAM': 'PCM_16',
122
+ 'W64': 'PCM_16',
123
+ 'MAT4': 'DOUBLE',
124
+ 'MAT5': 'DOUBLE',
125
+ 'PVF': 'PCM_16',
126
+ 'XI': 'DPCM_16',
127
+ 'HTK': 'PCM_16',
128
+ 'SDS': 'PCM_16',
129
+ 'AVR': 'PCM_16',
130
+ 'WAVEX': 'PCM_16',
131
+ 'SD2': 'PCM_16',
132
+ 'FLAC': 'PCM_16',
133
+ 'CAF': 'PCM_16',
134
+ 'WVE': 'ALAW',
135
+ 'OGG': 'VORBIS',
136
+ 'MPC2K': 'PCM_16',
137
+ 'RF64': 'PCM_16',
138
+ 'MP3': 'MPEG_LAYER_III',
139
+ }
140
+
141
+ _ffi_types = {
142
+ 'float64': 'double',
143
+ 'float32': 'float',
144
+ 'int32': 'int',
145
+ 'int16': 'short'
146
+ }
147
+
148
+ _bitrate_modes = {
149
+ 'CONSTANT': 0,
150
+ 'AVERAGE': 1,
151
+ 'VARIABLE': 2,
152
+ }
153
+
154
+ try: # packaged lib (in _soundfile_data which should be on python path)
155
+ if _sys.platform == 'darwin':
156
+ from platform import machine as _machine
157
+ _packaged_libname = 'libsndfile_' + _machine() + '.dylib'
158
+ elif _sys.platform == 'win32':
159
+ from platform import architecture as _architecture
160
+ from platform import machine as _machine
161
+ # this check can not be completed correctly: for x64 binaries running on
162
+ # arm64 Windows report the same values as arm64 binaries. For now, neither
163
+ # numpy nor cffi are available for arm64, so we can safely assume we're
164
+ # in x86 land:
165
+ if _architecture()[0] == '64bit':
166
+ _packaged_libname = 'libsndfile_x64.dll'
167
+ elif _architecture()[0] == '32bit':
168
+ _packaged_libname = 'libsndfile_x86.dll'
169
+ else:
170
+ raise OSError('no packaged library for Windows {} {}'
171
+ .format(_architecture(), _machine()))
172
+ elif _sys.platform == 'linux':
173
+ from platform import machine as _machine
174
+ if _machine() in ["aarch64", "aarch64_be", "armv8b", "armv8l"]:
175
+ _packaged_libname = 'libsndfile_arm64.so'
176
+ else:
177
+ _packaged_libname = 'libsndfile_' + _machine() + '.so'
178
+ else:
179
+ raise OSError('no packaged library for this platform')
180
+
181
+ import _soundfile_data # ImportError if this doesn't exist
182
+ _path = _os.path.dirname(_soundfile_data.__file__) # TypeError if __file__ is None
183
+ _full_path = _os.path.join(_path, _packaged_libname)
184
+ _snd = _ffi.dlopen(_full_path) # OSError if file doesn't exist or can't be loaded
185
+
186
+ except (OSError, ImportError, TypeError):
187
+ try: # system-wide libsndfile:
188
+ _libname = _find_library('sndfile')
189
+ if _libname is None:
190
+ raise OSError('sndfile library not found using ctypes.util.find_library')
191
+ _snd = _ffi.dlopen(_libname)
192
+
193
+ except OSError:
194
+ # Try explicit file name, if the general does not work (e.g. on nixos)
195
+ if _sys.platform == 'darwin':
196
+ _explicit_libname = 'libsndfile.dylib'
197
+ elif _sys.platform == 'win32':
198
+ _explicit_libname = 'libsndfile.dll'
199
+ elif _sys.platform == 'linux':
200
+ _explicit_libname = 'libsndfile.so'
201
+ else:
202
+ raise
203
+
204
+ # Homebrew on Apple M1 uses a `/opt/homebrew/lib` instead of
205
+ # `/usr/local/lib`. We are making sure we pick that up.
206
+ from platform import machine as _machine
207
+ if _sys.platform == 'darwin' and _machine() == 'arm64':
208
+ _hbrew_path = '/opt/homebrew/lib/' if _os.path.isdir('/opt/homebrew/lib/') \
209
+ else '/usr/local/lib/'
210
+ _snd = _ffi.dlopen(_os.path.join(_hbrew_path, _explicit_libname))
211
+ else:
212
+ _snd = _ffi.dlopen(_explicit_libname)
213
+
214
+ __libsndfile_version__ = _ffi.string(_snd.sf_version_string()).decode('utf-8', 'replace')
215
+ if __libsndfile_version__.startswith('libsndfile-'):
216
+ __libsndfile_version__ = __libsndfile_version__[len('libsndfile-'):]
217
+
218
+
219
+ def read(file, frames=-1, start=0, stop=None, dtype='float64', always_2d=False,
220
+ fill_value=None, out=None, samplerate=None, channels=None,
221
+ format=None, subtype=None, endian=None, closefd=True):
222
+ """Provide audio data from a sound file as NumPy array.
223
+
224
+ By default, the whole file is read from the beginning, but the
225
+ position to start reading can be specified with *start* and the
226
+ number of frames to read can be specified with *frames*.
227
+ Alternatively, a range can be specified with *start* and *stop*.
228
+
229
+ If there is less data left in the file than requested, the rest of
230
+ the frames are filled with *fill_value*.
231
+ If no *fill_value* is specified, a smaller array is returned.
232
+
233
+ Parameters
234
+ ----------
235
+ file : str or int or file-like object
236
+ The file to read from. See `SoundFile` for details.
237
+ frames : int, optional
238
+ The number of frames to read. If *frames* is negative, the whole
239
+ rest of the file is read. Not allowed if *stop* is given.
240
+ start : int, optional
241
+ Where to start reading. A negative value counts from the end.
242
+ stop : int, optional
243
+ The index after the last frame to be read. A negative value
244
+ counts from the end. Not allowed if *frames* is given.
245
+ dtype : {'float64', 'float32', 'int32', 'int16'}, optional
246
+ Data type of the returned array, by default ``'float64'``.
247
+ Floating point audio data is typically in the range from
248
+ ``-1.0`` to ``1.0``. Integer data is in the range from
249
+ ``-2**15`` to ``2**15-1`` for ``'int16'`` and from ``-2**31`` to
250
+ ``2**31-1`` for ``'int32'``.
251
+
252
+ .. note:: Reading int values from a float file will *not*
253
+ scale the data to [-1.0, 1.0). If the file contains
254
+ ``np.array([42.6], dtype='float32')``, you will read
255
+ ``np.array([43], dtype='int32')`` for ``dtype='int32'``.
256
+
257
+ Returns
258
+ -------
259
+ audiodata : `numpy.ndarray` or type(out)
260
+ A two-dimensional (frames x channels) NumPy array is returned.
261
+ If the sound file has only one channel, a one-dimensional array
262
+ is returned. Use ``always_2d=True`` to return a two-dimensional
263
+ array anyway.
264
+
265
+ If *out* was specified, it is returned. If *out* has more
266
+ frames than available in the file (or if *frames* is smaller
267
+ than the length of *out*) and no *fill_value* is given, then
268
+ only a part of *out* is overwritten and a view containing all
269
+ valid frames is returned.
270
+ samplerate : int
271
+ The sample rate of the audio file.
272
+
273
+ Other Parameters
274
+ ----------------
275
+ always_2d : bool, optional
276
+ By default, reading a mono sound file will return a
277
+ one-dimensional array. With ``always_2d=True``, audio data is
278
+ always returned as a two-dimensional array, even if the audio
279
+ file has only one channel.
280
+ fill_value : float, optional
281
+ If more frames are requested than available in the file, the
282
+ rest of the output is be filled with *fill_value*. If
283
+ *fill_value* is not specified, a smaller array is returned.
284
+ out : `numpy.ndarray` or subclass, optional
285
+ If *out* is specified, the data is written into the given array
286
+ instead of creating a new array. In this case, the arguments
287
+ *dtype* and *always_2d* are silently ignored! If *frames* is
288
+ not given, it is obtained from the length of *out*.
289
+ samplerate, channels, format, subtype, endian, closefd
290
+ See `SoundFile`.
291
+
292
+ Examples
293
+ --------
294
+ >>> import soundfile as sf
295
+ >>> data, samplerate = sf.read('stereo_file.wav')
296
+ >>> data
297
+ array([[ 0.71329652, 0.06294799],
298
+ [-0.26450912, -0.38874483],
299
+ ...
300
+ [ 0.67398441, -0.11516333]])
301
+ >>> samplerate
302
+ 44100
303
+
304
+ """
305
+ with SoundFile(file, 'r', samplerate, channels,
306
+ subtype, endian, format, closefd) as f:
307
+ frames = f._prepare_read(start, stop, frames)
308
+ data = f.read(frames, dtype, always_2d, fill_value, out)
309
+ return data, f.samplerate
310
+
311
+
312
+ def write(file, data, samplerate, subtype=None, endian=None, format=None,
313
+ closefd=True, compression_level=None, bitrate_mode=None):
314
+ """Write data to a sound file.
315
+
316
+ .. note:: If *file* exists, it will be truncated and overwritten!
317
+
318
+ Parameters
319
+ ----------
320
+ file : str or int or file-like object
321
+ The file to write to. See `SoundFile` for details.
322
+ data : array_like
323
+ The data to write. Usually two-dimensional (frames x channels),
324
+ but one-dimensional *data* can be used for mono files.
325
+ Only the data types ``'float64'``, ``'float32'``, ``'int32'``
326
+ and ``'int16'`` are supported.
327
+
328
+ .. note:: The data type of *data* does **not** select the data
329
+ type of the written file. Audio data will be
330
+ converted to the given *subtype*. Writing int values
331
+ to a float file will *not* scale the values to
332
+ [-1.0, 1.0). If you write the value ``np.array([42],
333
+ dtype='int32')``, to a ``subtype='FLOAT'`` file, the
334
+ file will then contain ``np.array([42.],
335
+ dtype='float32')``.
336
+
337
+ samplerate : int
338
+ The sample rate of the audio data.
339
+ subtype : str, optional
340
+ See `default_subtype()` for the default value and
341
+ `available_subtypes()` for all possible values.
342
+
343
+ Other Parameters
344
+ ----------------
345
+ format, endian, closefd, compression_level, bitrate_mode
346
+ See `SoundFile`.
347
+
348
+ Examples
349
+ --------
350
+ Write 10 frames of random data to a new file:
351
+
352
+ >>> import numpy as np
353
+ >>> import soundfile as sf
354
+ >>> sf.write('stereo_file.wav', np.random.randn(10, 2), 44100, 'PCM_24')
355
+
356
+ """
357
+ import numpy as np
358
+ data = np.asarray(data)
359
+ if data.ndim == 1:
360
+ channels = 1
361
+ else:
362
+ channels = data.shape[1]
363
+ with SoundFile(file, 'w', samplerate, channels,
364
+ subtype, endian, format, closefd,
365
+ compression_level, bitrate_mode) as f:
366
+ f.write(data)
367
+
368
+
369
+ def blocks(file, blocksize=None, overlap=0, frames=-1, start=0, stop=None,
370
+ dtype='float64', always_2d=False, fill_value=None, out=None,
371
+ samplerate=None, channels=None,
372
+ format=None, subtype=None, endian=None, closefd=True):
373
+ """Return a generator for block-wise reading.
374
+
375
+ By default, iteration starts at the beginning and stops at the end
376
+ of the file. Use *start* to start at a later position and *frames*
377
+ or *stop* to stop earlier.
378
+
379
+ If you stop iterating over the generator before it's exhausted,
380
+ the sound file is not closed. This is normally not a problem
381
+ because the file is opened in read-only mode. To close the file
382
+ properly, the generator's ``close()`` method can be called.
383
+
384
+ Parameters
385
+ ----------
386
+ file : str or int or file-like object
387
+ The file to read from. See `SoundFile` for details.
388
+ blocksize : int
389
+ The number of frames to read per block.
390
+ Either this or *out* must be given.
391
+ overlap : int, optional
392
+ The number of frames to rewind between each block.
393
+
394
+ Yields
395
+ ------
396
+ `numpy.ndarray` or type(out)
397
+ Blocks of audio data.
398
+ If *out* was given, and the requested frames are not an integer
399
+ multiple of the length of *out*, and no *fill_value* was given,
400
+ the last block will be a smaller view into *out*.
401
+
402
+ Other Parameters
403
+ ----------------
404
+ frames, start, stop
405
+ See `read()`.
406
+ dtype : {'float64', 'float32', 'int32', 'int16'}, optional
407
+ See `read()`.
408
+ always_2d, fill_value, out
409
+ See `read()`.
410
+ samplerate, channels, format, subtype, endian, closefd
411
+ See `SoundFile`.
412
+
413
+ Examples
414
+ --------
415
+ >>> import soundfile as sf
416
+ >>> for block in sf.blocks('stereo_file.wav', blocksize=1024):
417
+ >>> pass # do something with 'block'
418
+
419
+ """
420
+ with SoundFile(file, 'r', samplerate, channels,
421
+ subtype, endian, format, closefd) as f:
422
+ frames = f._prepare_read(start, stop, frames)
423
+ for block in f.blocks(blocksize, overlap, frames,
424
+ dtype, always_2d, fill_value, out):
425
+ yield block
426
+
427
+
428
+ class _SoundFileInfo(object):
429
+ """Information about a SoundFile"""
430
+
431
+ def __init__(self, file, verbose):
432
+ self.verbose = verbose
433
+ with SoundFile(file) as f:
434
+ self.name = f.name
435
+ self.samplerate = f.samplerate
436
+ self.channels = f.channels
437
+ self.frames = f.frames
438
+ self.duration = float(self.frames)/f.samplerate
439
+ self.format = f.format
440
+ self.subtype = f.subtype
441
+ self.endian = f.endian
442
+ self.format_info = f.format_info
443
+ self.subtype_info = f.subtype_info
444
+ self.sections = f.sections
445
+ self.extra_info = f.extra_info
446
+
447
+ @property
448
+ def _duration_str(self):
449
+ hours, rest = divmod(self.duration, 3600)
450
+ minutes, seconds = divmod(rest, 60)
451
+ if hours >= 1:
452
+ duration = "{0:.0g}:{1:02.0g}:{2:05.3f} h".format(hours, minutes, seconds)
453
+ elif minutes >= 1:
454
+ duration = "{0:02.0g}:{1:05.3f} min".format(minutes, seconds)
455
+ elif seconds <= 1:
456
+ duration = "{0:d} samples".format(self.frames)
457
+ else:
458
+ duration = "{0:.3f} s".format(seconds)
459
+ return duration
460
+
461
+ def __repr__(self):
462
+ info = "\n".join(
463
+ ["{0.name}",
464
+ "samplerate: {0.samplerate} Hz",
465
+ "channels: {0.channels}",
466
+ "duration: {0._duration_str}",
467
+ "format: {0.format_info} [{0.format}]",
468
+ "subtype: {0.subtype_info} [{0.subtype}]"])
469
+ if self.verbose:
470
+ info += "\n".join(
471
+ ["\nendian: {0.endian}",
472
+ "sections: {0.sections}",
473
+ "frames: {0.frames}",
474
+ 'extra_info: """',
475
+ ' {1}"""'])
476
+ indented_extra_info = ("\n"+" "*4).join(self.extra_info.split("\n"))
477
+ return info.format(self, indented_extra_info)
478
+
479
+
480
+ def info(file, verbose=False):
481
+ """Returns an object with information about a `SoundFile`.
482
+
483
+ Parameters
484
+ ----------
485
+ verbose : bool
486
+ Whether to print additional information.
487
+ """
488
+ return _SoundFileInfo(file, verbose)
489
+
490
+
491
+ def available_formats():
492
+ """Return a dictionary of available major formats.
493
+
494
+ Examples
495
+ --------
496
+ >>> import soundfile as sf
497
+ >>> sf.available_formats()
498
+ {'FLAC': 'FLAC (FLAC Lossless Audio Codec)',
499
+ 'OGG': 'OGG (OGG Container format)',
500
+ 'WAV': 'WAV (Microsoft)',
501
+ 'AIFF': 'AIFF (Apple/SGI)',
502
+ ...
503
+ 'WAVEX': 'WAVEX (Microsoft)',
504
+ 'RAW': 'RAW (header-less)',
505
+ 'MAT5': 'MAT5 (GNU Octave 2.1 / Matlab 5.0)'}
506
+
507
+ """
508
+ return dict(_available_formats_helper(_snd.SFC_GET_FORMAT_MAJOR_COUNT,
509
+ _snd.SFC_GET_FORMAT_MAJOR))
510
+
511
+
512
+ def available_subtypes(format=None):
513
+ """Return a dictionary of available subtypes.
514
+
515
+ Parameters
516
+ ----------
517
+ format : str
518
+ If given, only compatible subtypes are returned.
519
+
520
+ Examples
521
+ --------
522
+ >>> import soundfile as sf
523
+ >>> sf.available_subtypes('FLAC')
524
+ {'PCM_24': 'Signed 24 bit PCM',
525
+ 'PCM_16': 'Signed 16 bit PCM',
526
+ 'PCM_S8': 'Signed 8 bit PCM'}
527
+
528
+ """
529
+ subtypes = _available_formats_helper(_snd.SFC_GET_FORMAT_SUBTYPE_COUNT,
530
+ _snd.SFC_GET_FORMAT_SUBTYPE)
531
+ return dict((subtype, name) for subtype, name in subtypes
532
+ if format is None or check_format(format, subtype))
533
+
534
+
535
+ def check_format(format, subtype=None, endian=None):
536
+ """Check if the combination of format/subtype/endian is valid.
537
+
538
+ Examples
539
+ --------
540
+ >>> import soundfile as sf
541
+ >>> sf.check_format('WAV', 'PCM_24')
542
+ True
543
+ >>> sf.check_format('FLAC', 'VORBIS')
544
+ False
545
+
546
+ """
547
+ try:
548
+ return bool(_format_int(format, subtype, endian))
549
+ except (ValueError, TypeError):
550
+ return False
551
+
552
+
553
+ def default_subtype(format):
554
+ """Return the default subtype for a given format.
555
+
556
+ Examples
557
+ --------
558
+ >>> import soundfile as sf
559
+ >>> sf.default_subtype('WAV')
560
+ 'PCM_16'
561
+ >>> sf.default_subtype('MAT5')
562
+ 'DOUBLE'
563
+
564
+ """
565
+ _check_format(format)
566
+ return _default_subtypes.get(format.upper())
567
+
568
+
569
+ class SoundFile(object):
570
+ """A sound file.
571
+
572
+ For more documentation see the __init__() docstring (which is also
573
+ used for the online documentation (https://python-soundfile.readthedocs.io/).
574
+
575
+ """
576
+
577
+ def __init__(self, file, mode='r', samplerate=None, channels=None,
578
+ subtype=None, endian=None, format=None, closefd=True,
579
+ compression_level=None, bitrate_mode=None):
580
+ """Open a sound file.
581
+
582
+ If a file is opened with `mode` ``'r'`` (the default) or
583
+ ``'r+'``, no sample rate, channels or file format need to be
584
+ given because the information is obtained from the file. An
585
+ exception is the ``'RAW'`` data format, which always requires
586
+ these data points.
587
+
588
+ File formats consist of three case-insensitive strings:
589
+
590
+ * a *major format* which is by default obtained from the
591
+ extension of the file name (if known) and which can be
592
+ forced with the format argument (e.g. ``format='WAVEX'``).
593
+ * a *subtype*, e.g. ``'PCM_24'``. Most major formats have a
594
+ default subtype which is used if no subtype is specified.
595
+ * an *endian-ness*, which doesn't have to be specified at all in
596
+ most cases.
597
+
598
+ A `SoundFile` object is a *context manager*, which means
599
+ if used in a "with" statement, `close()` is automatically
600
+ called when reaching the end of the code block inside the "with"
601
+ statement.
602
+
603
+ Parameters
604
+ ----------
605
+ file : str or int or file-like object
606
+ The file to open. This can be a file name, a file
607
+ descriptor or a Python file object (or a similar object with
608
+ the methods ``read()``/``readinto()``, ``write()``,
609
+ ``seek()`` and ``tell()``).
610
+ mode : {'r', 'r+', 'w', 'w+', 'x', 'x+'}, optional
611
+ Open mode. Has to begin with one of these three characters:
612
+ ``'r'`` for reading, ``'w'`` for writing (truncates *file*)
613
+ or ``'x'`` for writing (raises an error if *file* already
614
+ exists). Additionally, it may contain ``'+'`` to open
615
+ *file* for both reading and writing.
616
+ The character ``'b'`` for *binary mode* is implied because
617
+ all sound files have to be opened in this mode.
618
+ If *file* is a file descriptor or a file-like object,
619
+ ``'w'`` doesn't truncate and ``'x'`` doesn't raise an error.
620
+ samplerate : int
621
+ The sample rate of the file. If `mode` contains ``'r'``,
622
+ this is obtained from the file (except for ``'RAW'`` files).
623
+ channels : int
624
+ The number of channels of the file.
625
+ If `mode` contains ``'r'``, this is obtained from the file
626
+ (except for ``'RAW'`` files).
627
+ subtype : str, sometimes optional
628
+ The subtype of the sound file. If `mode` contains ``'r'``,
629
+ this is obtained from the file (except for ``'RAW'``
630
+ files), if not, the default value depends on the selected
631
+ `format` (see `default_subtype()`).
632
+ See `available_subtypes()` for all possible subtypes for
633
+ a given `format`.
634
+ endian : {'FILE', 'LITTLE', 'BIG', 'CPU'}, sometimes optional
635
+ The endian-ness of the sound file. If `mode` contains
636
+ ``'r'``, this is obtained from the file (except for
637
+ ``'RAW'`` files), if not, the default value is ``'FILE'``,
638
+ which is correct in most cases.
639
+ format : str, sometimes optional
640
+ The major format of the sound file. If `mode` contains
641
+ ``'r'``, this is obtained from the file (except for
642
+ ``'RAW'`` files), if not, the default value is determined
643
+ from the file extension. See `available_formats()` for
644
+ all possible values.
645
+ closefd : bool, optional
646
+ Whether to close the file descriptor on `close()`. Only
647
+ applicable if the *file* argument is a file descriptor.
648
+ compression_level : float, optional
649
+ The compression level on 'write()'. The compression level
650
+ should be between 0.0 (minimum compression level) and 1.0
651
+ (highest compression level).
652
+ See `libsndfile document <https://github.com/libsndfile/libsndfile/blob/c81375f070f3c6764969a738eacded64f53a076e/docs/command.md>`__.
653
+ bitrate_mode : {'CONSTANT', 'AVERAGE', 'VARIABLE'}, optional
654
+ The bitrate mode on 'write()'.
655
+ See `libsndfile document <https://github.com/libsndfile/libsndfile/blob/c81375f070f3c6764969a738eacded64f53a076e/docs/command.md>`__.
656
+
657
+ Examples
658
+ --------
659
+ >>> from soundfile import SoundFile
660
+
661
+ Open an existing file for reading:
662
+
663
+ >>> myfile = SoundFile('existing_file.wav')
664
+ >>> # do something with myfile
665
+ >>> myfile.close()
666
+
667
+ Create a new sound file for reading and writing using a with
668
+ statement:
669
+
670
+ >>> with SoundFile('new_file.wav', 'x+', 44100, 2) as myfile:
671
+ >>> # do something with myfile
672
+ >>> # ...
673
+ >>> assert not myfile.closed
674
+ >>> # myfile.close() is called automatically at the end
675
+ >>> assert myfile.closed
676
+
677
+ """
678
+ # resolve PathLike objects (see PEP519 for details):
679
+ # can be replaced with _os.fspath(file) for Python >= 3.6
680
+ file = file.__fspath__() if hasattr(file, '__fspath__') else file
681
+ self._name = file
682
+ if mode is None:
683
+ mode = getattr(file, 'mode', None)
684
+ mode_int = _check_mode(mode)
685
+ self._mode = mode
686
+ self._compression_level = compression_level
687
+ self._bitrate_mode = bitrate_mode
688
+ self._info = _create_info_struct(file, mode, samplerate, channels,
689
+ format, subtype, endian)
690
+ self._file = self._open(file, mode_int, closefd)
691
+ if set(mode).issuperset('r+') and self.seekable():
692
+ # Move write position to 0 (like in Python file objects)
693
+ self.seek(0)
694
+ _snd.sf_command(self._file, _snd.SFC_SET_CLIPPING, _ffi.NULL,
695
+ _snd.SF_TRUE)
696
+
697
+ # set compression setting
698
+ if self._compression_level is not None:
699
+ # needs to be called before set_bitrate_mode
700
+ self._set_compression_level(self._compression_level)
701
+ if self._bitrate_mode is not None:
702
+ self._set_bitrate_mode(self._bitrate_mode)
703
+
704
+ name = property(lambda self: self._name)
705
+ """The file name of the sound file."""
706
+ mode = property(lambda self: self._mode)
707
+ """The open mode the sound file was opened with."""
708
+ samplerate = property(lambda self: self._info.samplerate)
709
+ """The sample rate of the sound file."""
710
+ frames = property(lambda self: self._info.frames)
711
+ """The number of frames in the sound file."""
712
+ channels = property(lambda self: self._info.channels)
713
+ """The number of channels in the sound file."""
714
+ format = property(
715
+ lambda self: _format_str(self._info.format & _snd.SF_FORMAT_TYPEMASK))
716
+ """The major format of the sound file."""
717
+ subtype = property(
718
+ lambda self: _format_str(self._info.format & _snd.SF_FORMAT_SUBMASK))
719
+ """The subtype of data in the the sound file."""
720
+ endian = property(
721
+ lambda self: _format_str(self._info.format & _snd.SF_FORMAT_ENDMASK))
722
+ """The endian-ness of the data in the sound file."""
723
+ format_info = property(
724
+ lambda self: _format_info(self._info.format &
725
+ _snd.SF_FORMAT_TYPEMASK)[1])
726
+ """A description of the major format of the sound file."""
727
+ subtype_info = property(
728
+ lambda self: _format_info(self._info.format &
729
+ _snd.SF_FORMAT_SUBMASK)[1])
730
+ """A description of the subtype of the sound file."""
731
+ sections = property(lambda self: self._info.sections)
732
+ """The number of sections of the sound file."""
733
+ closed = property(lambda self: self._file is None)
734
+ """Whether the sound file is closed or not."""
735
+ _errorcode = property(lambda self: _snd.sf_error(self._file))
736
+ """A pending sndfile error code."""
737
+ compression_level = property(lambda self: self._compression_level)
738
+ """The compression level on 'write()'"""
739
+ bitrate_mode = property(lambda self: self._bitrate_mode)
740
+ """The bitrate mode on 'write()'"""
741
+
742
+ @property
743
+ def extra_info(self):
744
+ """Retrieve the log string generated when opening the file."""
745
+ info = _ffi.new("char[]", 2**14)
746
+ _snd.sf_command(self._file, _snd.SFC_GET_LOG_INFO,
747
+ info, _ffi.sizeof(info))
748
+ return _ffi.string(info).decode('utf-8', 'replace')
749
+
750
+ # avoid confusion if something goes wrong before assigning self._file:
751
+ _file = None
752
+
753
+ def __repr__(self):
754
+ compression_setting = (", compression_level={0}".format(self.compression_level)
755
+ if self.compression_level is not None else "")
756
+ compression_setting += (", bitrate_mode='{0}'".format(self.bitrate_mode)
757
+ if self.bitrate_mode is not None else "")
758
+ return ("SoundFile({0.name!r}, mode={0.mode!r}, "
759
+ "samplerate={0.samplerate}, channels={0.channels}, "
760
+ "format={0.format!r}, subtype={0.subtype!r}, "
761
+ "endian={0.endian!r}{1})".format(self, compression_setting))
762
+
763
+ def __del__(self):
764
+ self.close()
765
+
766
+ def __enter__(self):
767
+ return self
768
+
769
+ def __exit__(self, *args):
770
+ self.close()
771
+
772
+ def __setattr__(self, name, value):
773
+ """Write text meta-data in the sound file through properties."""
774
+ if name in _str_types:
775
+ self._check_if_closed()
776
+ err = _snd.sf_set_string(self._file, _str_types[name],
777
+ value.encode())
778
+ _error_check(err)
779
+ else:
780
+ object.__setattr__(self, name, value)
781
+
782
+ def __getattr__(self, name):
783
+ """Read text meta-data in the sound file through properties."""
784
+ if name in _str_types:
785
+ self._check_if_closed()
786
+ data = _snd.sf_get_string(self._file, _str_types[name])
787
+ return _ffi.string(data).decode('utf-8', 'replace') if data else ""
788
+ else:
789
+ raise AttributeError(
790
+ "'SoundFile' object has no attribute {0!r}".format(name))
791
+
792
+ def __len__(self):
793
+ # Note: This is deprecated and will be removed at some point,
794
+ # see https://github.com/bastibe/python-soundfile/issues/199
795
+ return self._info.frames
796
+
797
+ def __bool__(self):
798
+ # Note: This is temporary until __len__ is removed, afterwards it
799
+ # can (and should) be removed without change of behavior
800
+ return True
801
+
802
+ def __nonzero__(self):
803
+ # Note: This is only for compatibility with Python 2 and it shall be
804
+ # removed at the same time as __bool__().
805
+ return self.__bool__()
806
+
807
+ def seekable(self):
808
+ """Return True if the file supports seeking."""
809
+ return self._info.seekable == _snd.SF_TRUE
810
+
811
+ def seek(self, frames, whence=SEEK_SET):
812
+ """Set the read/write position.
813
+
814
+ Parameters
815
+ ----------
816
+ frames : int
817
+ The frame index or offset to seek.
818
+ whence : {SEEK_SET, SEEK_CUR, SEEK_END}, optional
819
+ By default (``whence=SEEK_SET``), *frames* are counted from
820
+ the beginning of the file.
821
+ ``whence=SEEK_CUR`` seeks from the current position
822
+ (positive and negative values are allowed for *frames*).
823
+ ``whence=SEEK_END`` seeks from the end (use negative value
824
+ for *frames*).
825
+
826
+ Returns
827
+ -------
828
+ int
829
+ The new absolute read/write position in frames.
830
+
831
+ Examples
832
+ --------
833
+ >>> from soundfile import SoundFile, SEEK_END
834
+ >>> myfile = SoundFile('stereo_file.wav')
835
+
836
+ Seek to the beginning of the file:
837
+
838
+ >>> myfile.seek(0)
839
+ 0
840
+
841
+ Seek to the end of the file:
842
+
843
+ >>> myfile.seek(0, SEEK_END)
844
+ 44100 # this is the file length
845
+
846
+ """
847
+ self._check_if_closed()
848
+ position = _snd.sf_seek(self._file, frames, whence)
849
+ _error_check(self._errorcode)
850
+ return position
851
+
852
+ def tell(self):
853
+ """Return the current read/write position."""
854
+ return self.seek(0, SEEK_CUR)
855
+
856
+ def read(self, frames=-1, dtype='float64', always_2d=False,
857
+ fill_value=None, out=None):
858
+ """Read from the file and return data as NumPy array.
859
+
860
+ Reads the given number of frames in the given data format
861
+ starting at the current read/write position. This advances the
862
+ read/write position by the same number of frames.
863
+ By default, all frames from the current read/write position to
864
+ the end of the file are returned.
865
+ Use `seek()` to move the current read/write position.
866
+
867
+ Parameters
868
+ ----------
869
+ frames : int, optional
870
+ The number of frames to read. If ``frames < 0``, the whole
871
+ rest of the file is read.
872
+ dtype : {'float64', 'float32', 'int32', 'int16'}, optional
873
+ Data type of the returned array, by default ``'float64'``.
874
+ Floating point audio data is typically in the range from
875
+ ``-1.0`` to ``1.0``. Integer data is in the range from
876
+ ``-2**15`` to ``2**15-1`` for ``'int16'`` and from
877
+ ``-2**31`` to ``2**31-1`` for ``'int32'``.
878
+
879
+ .. note:: Reading int values from a float file will *not*
880
+ scale the data to [-1.0, 1.0). If the file contains
881
+ ``np.array([42.6], dtype='float32')``, you will read
882
+ ``np.array([43], dtype='int32')`` for
883
+ ``dtype='int32'``.
884
+
885
+ Returns
886
+ -------
887
+ audiodata : `numpy.ndarray` or type(out)
888
+ A two-dimensional NumPy (frames x channels) array is
889
+ returned. If the sound file has only one channel, a
890
+ one-dimensional array is returned. Use ``always_2d=True``
891
+ to return a two-dimensional array anyway.
892
+
893
+ If *out* was specified, it is returned. If *out* has more
894
+ frames than available in the file (or if *frames* is
895
+ smaller than the length of *out*) and no *fill_value* is
896
+ given, then only a part of *out* is overwritten and a view
897
+ containing all valid frames is returned.
898
+
899
+ Other Parameters
900
+ ----------------
901
+ always_2d : bool, optional
902
+ By default, reading a mono sound file will return a
903
+ one-dimensional array. With ``always_2d=True``, audio data
904
+ is always returned as a two-dimensional array, even if the
905
+ audio file has only one channel.
906
+ fill_value : float, optional
907
+ If more frames are requested than available in the file,
908
+ the rest of the output is be filled with *fill_value*. If
909
+ *fill_value* is not specified, a smaller array is
910
+ returned.
911
+ out : `numpy.ndarray` or subclass, optional
912
+ If *out* is specified, the data is written into the given
913
+ array instead of creating a new array. In this case, the
914
+ arguments *dtype* and *always_2d* are silently ignored! If
915
+ *frames* is not given, it is obtained from the length of
916
+ *out*.
917
+
918
+ Examples
919
+ --------
920
+ >>> from soundfile import SoundFile
921
+ >>> myfile = SoundFile('stereo_file.wav')
922
+
923
+ Reading 3 frames from a stereo file:
924
+
925
+ >>> myfile.read(3)
926
+ array([[ 0.71329652, 0.06294799],
927
+ [-0.26450912, -0.38874483],
928
+ [ 0.67398441, -0.11516333]])
929
+ >>> myfile.close()
930
+
931
+ See Also
932
+ --------
933
+ buffer_read, .write
934
+
935
+ """
936
+ if out is None:
937
+ frames = self._check_frames(frames, fill_value)
938
+ out = self._create_empty_array(frames, always_2d, dtype)
939
+ else:
940
+ if frames < 0 or frames > len(out):
941
+ frames = len(out)
942
+ frames = self._array_io('read', out, frames)
943
+ if len(out) > frames:
944
+ if fill_value is None:
945
+ out = out[:frames]
946
+ else:
947
+ out[frames:] = fill_value
948
+ return out
949
+
950
+ def buffer_read(self, frames=-1, dtype=None):
951
+ """Read from the file and return data as buffer object.
952
+
953
+ Reads the given number of *frames* in the given data format
954
+ starting at the current read/write position. This advances the
955
+ read/write position by the same number of frames.
956
+ By default, all frames from the current read/write position to
957
+ the end of the file are returned.
958
+ Use `seek()` to move the current read/write position.
959
+
960
+ Parameters
961
+ ----------
962
+ frames : int, optional
963
+ The number of frames to read. If ``frames < 0``, the whole
964
+ rest of the file is read.
965
+ dtype : {'float64', 'float32', 'int32', 'int16'}
966
+ Audio data will be converted to the given data type.
967
+
968
+ Returns
969
+ -------
970
+ buffer
971
+ A buffer containing the read data.
972
+
973
+ See Also
974
+ --------
975
+ buffer_read_into, .read, buffer_write
976
+
977
+ """
978
+ frames = self._check_frames(frames, fill_value=None)
979
+ ctype = self._check_dtype(dtype)
980
+ cdata = _ffi.new(ctype + '[]', frames * self.channels)
981
+ read_frames = self._cdata_io('read', cdata, ctype, frames)
982
+ assert read_frames == frames
983
+ return _ffi.buffer(cdata)
984
+
985
+ def buffer_read_into(self, buffer, dtype):
986
+ """Read from the file into a given buffer object.
987
+
988
+ Fills the given *buffer* with frames in the given data format
989
+ starting at the current read/write position (which can be
990
+ changed with `seek()`) until the buffer is full or the end
991
+ of the file is reached. This advances the read/write position
992
+ by the number of frames that were read.
993
+
994
+ Parameters
995
+ ----------
996
+ buffer : writable buffer
997
+ Audio frames from the file are written to this buffer.
998
+ dtype : {'float64', 'float32', 'int32', 'int16'}
999
+ The data type of *buffer*.
1000
+
1001
+ Returns
1002
+ -------
1003
+ int
1004
+ The number of frames that were read from the file.
1005
+ This can be less than the size of *buffer*.
1006
+ The rest of the buffer is not filled with meaningful data.
1007
+
1008
+ See Also
1009
+ --------
1010
+ buffer_read, .read
1011
+
1012
+ """
1013
+ ctype = self._check_dtype(dtype)
1014
+ cdata, frames = self._check_buffer(buffer, ctype)
1015
+ frames = self._cdata_io('read', cdata, ctype, frames)
1016
+ return frames
1017
+
1018
+ def write(self, data):
1019
+ """Write audio data from a NumPy array to the file.
1020
+
1021
+ Writes a number of frames at the read/write position to the
1022
+ file. This also advances the read/write position by the same
1023
+ number of frames and enlarges the file if necessary.
1024
+
1025
+ Note that writing int values to a float file will *not* scale
1026
+ the values to [-1.0, 1.0). If you write the value
1027
+ ``np.array([42], dtype='int32')``, to a ``subtype='FLOAT'``
1028
+ file, the file will then contain ``np.array([42.],
1029
+ dtype='float32')``.
1030
+
1031
+ Parameters
1032
+ ----------
1033
+ data : array_like
1034
+ The data to write. Usually two-dimensional (frames x
1035
+ channels), but one-dimensional *data* can be used for mono
1036
+ files. Only the data types ``'float64'``, ``'float32'``,
1037
+ ``'int32'`` and ``'int16'`` are supported.
1038
+
1039
+ .. note:: The data type of *data* does **not** select the
1040
+ data type of the written file. Audio data will be
1041
+ converted to the given *subtype*. Writing int values
1042
+ to a float file will *not* scale the values to
1043
+ [-1.0, 1.0). If you write the value ``np.array([42],
1044
+ dtype='int32')``, to a ``subtype='FLOAT'`` file, the
1045
+ file will then contain ``np.array([42.],
1046
+ dtype='float32')``.
1047
+
1048
+ Examples
1049
+ --------
1050
+ >>> import numpy as np
1051
+ >>> from soundfile import SoundFile
1052
+ >>> myfile = SoundFile('stereo_file.wav')
1053
+
1054
+ Write 10 frames of random data to a new file:
1055
+
1056
+ >>> with SoundFile('stereo_file.wav', 'w', 44100, 2, 'PCM_24') as f:
1057
+ >>> f.write(np.random.randn(10, 2))
1058
+
1059
+ See Also
1060
+ --------
1061
+ buffer_write, .read
1062
+
1063
+ """
1064
+ import numpy as np
1065
+
1066
+ # no copy is made if data has already the correct memory layout:
1067
+ data = np.ascontiguousarray(data)
1068
+ written = self._array_io('write', data, len(data))
1069
+ assert written == len(data)
1070
+ self._update_frames(written)
1071
+
1072
+ def buffer_write(self, data, dtype):
1073
+ """Write audio data from a buffer/bytes object to the file.
1074
+
1075
+ Writes the contents of *data* to the file at the current
1076
+ read/write position.
1077
+ This also advances the read/write position by the number of
1078
+ frames that were written and enlarges the file if necessary.
1079
+
1080
+ Parameters
1081
+ ----------
1082
+ data : buffer or bytes
1083
+ A buffer or bytes object containing the audio data to be
1084
+ written.
1085
+ dtype : {'float64', 'float32', 'int32', 'int16'}
1086
+ The data type of the audio data stored in *data*.
1087
+
1088
+ See Also
1089
+ --------
1090
+ .write, buffer_read
1091
+
1092
+ """
1093
+ ctype = self._check_dtype(dtype)
1094
+ cdata, frames = self._check_buffer(data, ctype)
1095
+ written = self._cdata_io('write', cdata, ctype, frames)
1096
+ assert written == frames
1097
+ self._update_frames(written)
1098
+
1099
+ def blocks(self, blocksize=None, overlap=0, frames=-1, dtype='float64',
1100
+ always_2d=False, fill_value=None, out=None):
1101
+ """Return a generator for block-wise reading.
1102
+
1103
+ By default, the generator yields blocks of the given
1104
+ *blocksize* (using a given *overlap*) until the end of the file
1105
+ is reached; *frames* can be used to stop earlier.
1106
+
1107
+ Parameters
1108
+ ----------
1109
+ blocksize : int
1110
+ The number of frames to read per block. Either this or *out*
1111
+ must be given.
1112
+ overlap : int, optional
1113
+ The number of frames to rewind between each block.
1114
+ frames : int, optional
1115
+ The number of frames to read.
1116
+ If ``frames < 0``, the file is read until the end.
1117
+ dtype : {'float64', 'float32', 'int32', 'int16'}, optional
1118
+ See `read()`.
1119
+
1120
+ Yields
1121
+ ------
1122
+ `numpy.ndarray` or type(out)
1123
+ Blocks of audio data.
1124
+ If *out* was given, and the requested frames are not an
1125
+ integer multiple of the length of *out*, and no
1126
+ *fill_value* was given, the last block will be a smaller
1127
+ view into *out*.
1128
+
1129
+
1130
+ Other Parameters
1131
+ ----------------
1132
+ always_2d, fill_value, out
1133
+ See `read()`.
1134
+ fill_value : float, optional
1135
+ See `read()`.
1136
+ out : `numpy.ndarray` or subclass, optional
1137
+ If *out* is specified, the data is written into the given
1138
+ array instead of creating a new array. In this case, the
1139
+ arguments *dtype* and *always_2d* are silently ignored!
1140
+
1141
+ Examples
1142
+ --------
1143
+ >>> from soundfile import SoundFile
1144
+ >>> with SoundFile('stereo_file.wav') as f:
1145
+ >>> for block in f.blocks(blocksize=1024):
1146
+ >>> pass # do something with 'block'
1147
+
1148
+ """
1149
+ import numpy as np
1150
+
1151
+ if 'r' not in self.mode and '+' not in self.mode:
1152
+ raise SoundFileRuntimeError("blocks() is not allowed in write-only mode")
1153
+
1154
+ frames = self._check_frames(frames, fill_value)
1155
+ if out is None:
1156
+ if blocksize is None:
1157
+ raise TypeError("One of {blocksize, out} must be specified")
1158
+ out_size = blocksize if fill_value is not None else min(blocksize, frames)
1159
+ out = self._create_empty_array(out_size, always_2d, dtype)
1160
+ copy_out = True
1161
+ else:
1162
+ if blocksize is not None:
1163
+ raise TypeError(
1164
+ "Only one of {blocksize, out} may be specified")
1165
+ blocksize = len(out)
1166
+ copy_out = False
1167
+
1168
+ overlap_memory = None
1169
+ while frames > 0:
1170
+ if overlap_memory is None:
1171
+ output_offset = 0
1172
+ else:
1173
+ output_offset = len(overlap_memory)
1174
+ out[:output_offset] = overlap_memory
1175
+
1176
+ toread = min(blocksize - output_offset, frames)
1177
+ self.read(toread, dtype, always_2d, fill_value, out[output_offset:])
1178
+
1179
+ if overlap:
1180
+ if overlap_memory is None:
1181
+ overlap_memory = np.copy(out[-overlap:])
1182
+ else:
1183
+ overlap_memory[:] = out[-overlap:]
1184
+
1185
+ if blocksize > frames + overlap and fill_value is None:
1186
+ block = out[:frames + overlap]
1187
+ else:
1188
+ block = out
1189
+ yield np.copy(block) if copy_out else block
1190
+ frames -= toread
1191
+
1192
+ def truncate(self, frames=None):
1193
+ """Truncate the file to a given number of frames.
1194
+
1195
+ After this command, the read/write position will be at the new
1196
+ end of the file.
1197
+
1198
+ Parameters
1199
+ ----------
1200
+ frames : int, optional
1201
+ Only the data before *frames* is kept, the rest is deleted.
1202
+ If not specified, the current read/write position is used.
1203
+
1204
+ """
1205
+ if frames is None:
1206
+ frames = self.tell()
1207
+ err = _snd.sf_command(self._file, _snd.SFC_FILE_TRUNCATE,
1208
+ _ffi.new("sf_count_t*", frames),
1209
+ _ffi.sizeof("sf_count_t"))
1210
+ if err:
1211
+ # get the actual error code
1212
+ err = _snd.sf_error(self._file)
1213
+ raise LibsndfileError(err, "Error truncating the file")
1214
+ self._info.frames = frames
1215
+
1216
+ def flush(self):
1217
+ """Write unwritten data to the file system.
1218
+
1219
+ Data written with `write()` is not immediately written to
1220
+ the file system but buffered in memory to be written at a later
1221
+ time. Calling `flush()` makes sure that all changes are
1222
+ actually written to the file system.
1223
+
1224
+ This has no effect on files opened in read-only mode.
1225
+
1226
+ """
1227
+ self._check_if_closed()
1228
+ _snd.sf_write_sync(self._file)
1229
+
1230
+ def close(self):
1231
+ """Close the file. Can be called multiple times."""
1232
+ if not self.closed:
1233
+ # be sure to flush data to disk before closing the file
1234
+ self.flush()
1235
+ err = _snd.sf_close(self._file)
1236
+ self._file = None
1237
+ _error_check(err)
1238
+
1239
+ def _open(self, file, mode_int, closefd):
1240
+ """Call the appropriate sf_open*() function from libsndfile."""
1241
+ if isinstance(file, (_unicode, bytes)):
1242
+ if _os.path.isfile(file):
1243
+ if 'x' in self.mode:
1244
+ raise OSError("File exists: {0!r}".format(self.name))
1245
+ elif set(self.mode).issuperset('w+'):
1246
+ # truncate the file, because SFM_RDWR doesn't:
1247
+ _os.close(_os.open(file, _os.O_WRONLY | _os.O_TRUNC))
1248
+ openfunction = _snd.sf_open
1249
+ if isinstance(file, _unicode):
1250
+ if _sys.platform == 'win32':
1251
+ openfunction = _snd.sf_wchar_open
1252
+ else:
1253
+ file = file.encode(_sys.getfilesystemencoding())
1254
+ file_ptr = openfunction(file, mode_int, self._info)
1255
+ elif isinstance(file, int):
1256
+ file_ptr = _snd.sf_open_fd(file, mode_int, self._info, closefd)
1257
+ elif _has_virtual_io_attrs(file, mode_int):
1258
+ file_ptr = _snd.sf_open_virtual(self._init_virtual_io(file),
1259
+ mode_int, self._info, _ffi.NULL)
1260
+ else:
1261
+ raise TypeError("Invalid file: {0!r}".format(self.name))
1262
+ if file_ptr == _ffi.NULL:
1263
+ # get the actual error code
1264
+ err = _snd.sf_error(file_ptr)
1265
+ raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
1266
+ if mode_int == _snd.SFM_WRITE:
1267
+ # Due to a bug in libsndfile version <= 1.0.25, frames != 0
1268
+ # when opening a named pipe in SFM_WRITE mode.
1269
+ # See http://github.com/erikd/libsndfile/issues/77.
1270
+ self._info.frames = 0
1271
+ # This is not necessary for "normal" files (because
1272
+ # frames == 0 in this case), but it doesn't hurt, either.
1273
+ return file_ptr
1274
+
1275
+ def _init_virtual_io(self, file):
1276
+ """Initialize callback functions for sf_open_virtual()."""
1277
+ @_ffi.callback("sf_vio_get_filelen")
1278
+ def vio_get_filelen(user_data):
1279
+ curr = file.tell()
1280
+ file.seek(0, SEEK_END)
1281
+ size = file.tell()
1282
+ file.seek(curr, SEEK_SET)
1283
+ return size
1284
+
1285
+ @_ffi.callback("sf_vio_seek")
1286
+ def vio_seek(offset, whence, user_data):
1287
+ file.seek(offset, whence)
1288
+ return file.tell()
1289
+
1290
+ @_ffi.callback("sf_vio_read")
1291
+ def vio_read(ptr, count, user_data):
1292
+ # first try readinto(), if not available fall back to read()
1293
+ try:
1294
+ buf = _ffi.buffer(ptr, count)
1295
+ data_read = file.readinto(buf)
1296
+ except AttributeError:
1297
+ data = file.read(count)
1298
+ data_read = len(data)
1299
+ buf = _ffi.buffer(ptr, data_read)
1300
+ buf[0:data_read] = data
1301
+ return data_read
1302
+
1303
+ @_ffi.callback("sf_vio_write")
1304
+ def vio_write(ptr, count, user_data):
1305
+ buf = _ffi.buffer(ptr, count)
1306
+ data = buf[:]
1307
+ written = file.write(data)
1308
+ # write() returns None for file objects in Python <= 2.7:
1309
+ if written is None:
1310
+ written = count
1311
+ return written
1312
+
1313
+ @_ffi.callback("sf_vio_tell")
1314
+ def vio_tell(user_data):
1315
+ return file.tell()
1316
+
1317
+ # Note: the callback functions must be kept alive!
1318
+ self._virtual_io = {'get_filelen': vio_get_filelen,
1319
+ 'seek': vio_seek,
1320
+ 'read': vio_read,
1321
+ 'write': vio_write,
1322
+ 'tell': vio_tell}
1323
+
1324
+ return _ffi.new("SF_VIRTUAL_IO*", self._virtual_io)
1325
+
1326
+ def _getAttributeNames(self):
1327
+ """Return all attributes used in __setattr__ and __getattr__.
1328
+
1329
+ This is useful for auto-completion (e.g. IPython).
1330
+
1331
+ """
1332
+ return _str_types
1333
+
1334
+ def _check_if_closed(self):
1335
+ """Check if the file is closed and raise an error if it is.
1336
+
1337
+ This should be used in every method that uses self._file.
1338
+
1339
+ """
1340
+ if self.closed:
1341
+ raise SoundFileRuntimeError("I/O operation on closed file")
1342
+
1343
+ def _check_frames(self, frames, fill_value):
1344
+ """Reduce frames to no more than are available in the file."""
1345
+ if self.seekable():
1346
+ remaining_frames = self.frames - self.tell()
1347
+ if frames < 0 or (frames > remaining_frames and
1348
+ fill_value is None):
1349
+ frames = remaining_frames
1350
+ elif frames < 0:
1351
+ raise ValueError("frames must be specified for non-seekable files")
1352
+ return frames
1353
+
1354
+ def _check_buffer(self, data, ctype):
1355
+ """Convert buffer to cdata and check for valid size."""
1356
+ assert ctype in _ffi_types.values()
1357
+ if not isinstance(data, bytes):
1358
+ data = _ffi.from_buffer(data)
1359
+ frames, remainder = divmod(len(data),
1360
+ self.channels * _ffi.sizeof(ctype))
1361
+ if remainder:
1362
+ raise ValueError("Data size must be a multiple of frame size")
1363
+ return data, frames
1364
+
1365
+ def _create_empty_array(self, frames, always_2d, dtype):
1366
+ """Create an empty array with appropriate shape."""
1367
+ import numpy as np
1368
+ if always_2d or self.channels > 1:
1369
+ shape = frames, self.channels
1370
+ else:
1371
+ shape = frames,
1372
+ return np.empty(shape, dtype, order='C')
1373
+
1374
+ def _check_dtype(self, dtype):
1375
+ """Check if dtype string is valid and return ctype string."""
1376
+ try:
1377
+ return _ffi_types[dtype]
1378
+ except KeyError:
1379
+ raise ValueError("dtype must be one of {0!r} and not {1!r}".format(
1380
+ sorted(_ffi_types.keys()), dtype))
1381
+
1382
+ def _array_io(self, action, array, frames):
1383
+ """Check array and call low-level IO function."""
1384
+ if array.ndim not in (1,2):
1385
+ raise ValueError("Invalid shape: {0!r} ({1})".format(array.shape, "0 dimensions not supported" if array.ndim < 1 else "too many dimensions"))
1386
+ array_channels = 1 if array.ndim == 1 else array.shape[1]
1387
+ if array_channels != self.channels:
1388
+ raise ValueError("Invalid shape: {0!r} (Expected {1} channels, got {2})".format(array.shape, self.channels, array_channels))
1389
+ if not array.flags.c_contiguous:
1390
+ raise ValueError("Data must be C-contiguous")
1391
+ ctype = self._check_dtype(array.dtype.name)
1392
+ assert array.dtype.itemsize == _ffi.sizeof(ctype)
1393
+ cdata = _ffi.cast(ctype + '*', array.__array_interface__['data'][0])
1394
+ return self._cdata_io(action, cdata, ctype, frames)
1395
+
1396
+ def _cdata_io(self, action, data, ctype, frames):
1397
+ """Call one of libsndfile's read/write functions."""
1398
+ assert ctype in _ffi_types.values()
1399
+ self._check_if_closed()
1400
+ if self.seekable():
1401
+ curr = self.tell()
1402
+ func = getattr(_snd, 'sf_' + action + 'f_' + ctype)
1403
+ frames = func(self._file, data, frames)
1404
+ _error_check(self._errorcode)
1405
+ if self.seekable():
1406
+ self.seek(curr + frames, SEEK_SET) # Update read & write position
1407
+ return frames
1408
+
1409
+ def _update_frames(self, written):
1410
+ """Update self.frames after writing."""
1411
+ if self.seekable():
1412
+ curr = self.tell()
1413
+ self._info.frames = self.seek(0, SEEK_END)
1414
+ self.seek(curr, SEEK_SET)
1415
+ else:
1416
+ self._info.frames += written
1417
+
1418
+ def _prepare_read(self, start, stop, frames):
1419
+ """Seek to start frame and calculate length."""
1420
+ if start != 0 and not self.seekable():
1421
+ raise ValueError("start is only allowed for seekable files")
1422
+ if frames >= 0 and stop is not None:
1423
+ raise TypeError("Only one of {frames, stop} may be used")
1424
+
1425
+ start, stop, _ = slice(start, stop).indices(self.frames)
1426
+ if stop < start:
1427
+ stop = start
1428
+ if frames < 0:
1429
+ frames = stop - start
1430
+ if self.seekable():
1431
+ self.seek(start, SEEK_SET)
1432
+ return frames
1433
+
1434
+ def copy_metadata(self):
1435
+ """Get all metadata present in this SoundFile
1436
+
1437
+ Returns
1438
+ -------
1439
+
1440
+ metadata: dict[str, str]
1441
+ A dict with all metadata. Possible keys are: 'title', 'copyright',
1442
+ 'software', 'artist', 'comment', 'date', 'album', 'license',
1443
+ 'tracknumber' and 'genre'.
1444
+ """
1445
+ strs = {}
1446
+ for strtype, strid in _str_types.items():
1447
+ data = _snd.sf_get_string(self._file, strid)
1448
+ if data:
1449
+ strs[strtype] = _ffi.string(data).decode('utf-8', 'replace')
1450
+ return strs
1451
+
1452
+ def _set_bitrate_mode(self, bitrate_mode):
1453
+ """Call libsndfile's set bitrate mode function."""
1454
+ assert bitrate_mode in _bitrate_modes
1455
+
1456
+ pointer_bitrate_mode = _ffi.new("int[1]")
1457
+ pointer_bitrate_mode[0] = _bitrate_modes[bitrate_mode]
1458
+ err = _snd.sf_command(self._file, _snd.SFC_SET_BITRATE_MODE, pointer_bitrate_mode, _ffi.sizeof(pointer_bitrate_mode))
1459
+ if err != _snd.SF_TRUE:
1460
+ err = _snd.sf_error(self._file)
1461
+ raise LibsndfileError(err, f"Error set bitrate mode {bitrate_mode}")
1462
+
1463
+
1464
+ def _set_compression_level(self, compression_level):
1465
+ """Call libsndfile's set compression level function."""
1466
+ if not (0 <= compression_level <= 1):
1467
+ raise ValueError("Compression level must be in range [0..1]")
1468
+
1469
+ pointer_compression_level = _ffi.new("double[1]")
1470
+ pointer_compression_level[0] = compression_level
1471
+ err = _snd.sf_command(self._file, _snd.SFC_SET_COMPRESSION_LEVEL, pointer_compression_level, _ffi.sizeof(pointer_compression_level))
1472
+ if err != _snd.SF_TRUE:
1473
+ err = _snd.sf_error(self._file)
1474
+ raise LibsndfileError(err, f"Error set compression level {compression_level}")
1475
+
1476
+
1477
+ def _error_check(err, prefix=""):
1478
+ """Raise LibsndfileError if there is an error."""
1479
+ if err != 0:
1480
+ raise LibsndfileError(err, prefix=prefix)
1481
+
1482
+
1483
+ def _format_int(format, subtype, endian):
1484
+ """Return numeric ID for given format|subtype|endian combo."""
1485
+ result = _check_format(format)
1486
+ if subtype is None:
1487
+ subtype = default_subtype(format)
1488
+ if subtype is None:
1489
+ raise TypeError(
1490
+ "No default subtype for major format {0!r}".format(format))
1491
+ elif not isinstance(subtype, (_unicode, str)):
1492
+ raise TypeError("Invalid subtype: {0!r}".format(subtype))
1493
+ try:
1494
+ result |= _subtypes[subtype.upper()]
1495
+ except KeyError:
1496
+ raise ValueError("Unknown subtype: {0!r}".format(subtype))
1497
+ if endian is None:
1498
+ endian = 'FILE'
1499
+ elif not isinstance(endian, (_unicode, str)):
1500
+ raise TypeError("Invalid endian-ness: {0!r}".format(endian))
1501
+ try:
1502
+ result |= _endians[endian.upper()]
1503
+ except KeyError:
1504
+ raise ValueError("Unknown endian-ness: {0!r}".format(endian))
1505
+
1506
+ info = _ffi.new("SF_INFO*")
1507
+ info.format = result
1508
+ info.channels = 1
1509
+ if _snd.sf_format_check(info) == _snd.SF_FALSE:
1510
+ raise ValueError(
1511
+ "Invalid combination of format, subtype and endian")
1512
+ return result
1513
+
1514
+
1515
+ def _check_mode(mode):
1516
+ """Check if mode is valid and return its integer representation."""
1517
+ if not isinstance(mode, (_unicode, str)):
1518
+ raise TypeError("Invalid mode: {0!r}".format(mode))
1519
+ mode_set = set(mode)
1520
+ if mode_set.difference('xrwb+') or len(mode) > len(mode_set):
1521
+ raise ValueError("Invalid mode: {0!r}".format(mode))
1522
+ if len(mode_set.intersection('xrw')) != 1:
1523
+ raise ValueError("mode must contain exactly one of 'xrw'")
1524
+
1525
+ if '+' in mode_set:
1526
+ mode_int = _snd.SFM_RDWR
1527
+ elif 'r' in mode_set:
1528
+ mode_int = _snd.SFM_READ
1529
+ else:
1530
+ mode_int = _snd.SFM_WRITE
1531
+ return mode_int
1532
+
1533
+
1534
+ def _create_info_struct(file, mode, samplerate, channels,
1535
+ format, subtype, endian):
1536
+ """Check arguments and create SF_INFO struct."""
1537
+ original_format = format
1538
+ if format is None:
1539
+ format = _get_format_from_filename(file, mode)
1540
+ assert isinstance(format, (_unicode, str))
1541
+ else:
1542
+ _check_format(format)
1543
+
1544
+ info = _ffi.new("SF_INFO*")
1545
+ if 'r' not in mode or format.upper() == 'RAW':
1546
+ if samplerate is None:
1547
+ raise TypeError("samplerate must be specified")
1548
+ info.samplerate = samplerate
1549
+ if channels is None:
1550
+ raise TypeError("channels must be specified")
1551
+ info.channels = channels
1552
+ info.format = _format_int(format, subtype, endian)
1553
+ else:
1554
+ if any(arg is not None for arg in (
1555
+ samplerate, channels, original_format, subtype, endian)):
1556
+ raise TypeError("Not allowed for existing files (except 'RAW'): "
1557
+ "samplerate, channels, format, subtype, endian")
1558
+ return info
1559
+
1560
+
1561
+ def _get_format_from_filename(file, mode):
1562
+ """Return a format string obtained from file (or file.name).
1563
+
1564
+ If file already exists (= read mode), an empty string is returned on
1565
+ error. If not, an exception is raised.
1566
+ The return type will always be str or unicode (even if
1567
+ file/file.name is a bytes object).
1568
+
1569
+ """
1570
+ format = ''
1571
+ file = getattr(file, 'name', file)
1572
+ try:
1573
+ # This raises an exception if file is not a (Unicode/byte) string:
1574
+ format = _os.path.splitext(file)[-1][1:]
1575
+ # Convert bytes to unicode (raises AttributeError on Python 3 str):
1576
+ format = format.decode('utf-8', 'replace')
1577
+ except Exception:
1578
+ pass
1579
+ if format.upper() not in _formats and 'r' not in mode:
1580
+ raise TypeError("No format specified and unable to get format from "
1581
+ "file extension: {0!r}".format(file))
1582
+ return format
1583
+
1584
+
1585
+ def _format_str(format_int):
1586
+ """Return the string representation of a given numeric format."""
1587
+ for dictionary in _formats, _subtypes, _endians:
1588
+ for k, v in dictionary.items():
1589
+ if v == format_int:
1590
+ return k
1591
+ else:
1592
+ return 'n/a'
1593
+
1594
+
1595
+ def _format_info(format_int, format_flag=_snd.SFC_GET_FORMAT_INFO):
1596
+ """Return the ID and short description of a given format."""
1597
+ format_info = _ffi.new("SF_FORMAT_INFO*")
1598
+ format_info.format = format_int
1599
+ _snd.sf_command(_ffi.NULL, format_flag, format_info,
1600
+ _ffi.sizeof("SF_FORMAT_INFO"))
1601
+ name = format_info.name
1602
+ return (_format_str(format_info.format),
1603
+ _ffi.string(name).decode('utf-8', 'replace') if name else "")
1604
+
1605
+
1606
+ def _available_formats_helper(count_flag, format_flag):
1607
+ """Helper for available_formats() and available_subtypes()."""
1608
+ count = _ffi.new("int*")
1609
+ _snd.sf_command(_ffi.NULL, count_flag, count, _ffi.sizeof("int"))
1610
+ for format_int in range(count[0]):
1611
+ yield _format_info(format_int, format_flag)
1612
+
1613
+
1614
+ def _check_format(format_str):
1615
+ """Check if `format_str` is valid and return format ID."""
1616
+ if not isinstance(format_str, (_unicode, str)):
1617
+ raise TypeError("Invalid format: {0!r}".format(format_str))
1618
+ try:
1619
+ format_int = _formats[format_str.upper()]
1620
+ except KeyError:
1621
+ raise ValueError("Unknown format: {0!r}".format(format_str))
1622
+ return format_int
1623
+
1624
+
1625
+ def _has_virtual_io_attrs(file, mode_int):
1626
+ """Check if file has all the necessary attributes for virtual IO."""
1627
+ readonly = mode_int == _snd.SFM_READ
1628
+ writeonly = mode_int == _snd.SFM_WRITE
1629
+ return all([
1630
+ hasattr(file, 'seek'),
1631
+ hasattr(file, 'tell'),
1632
+ hasattr(file, 'write') or readonly,
1633
+ hasattr(file, 'read') or hasattr(file, 'readinto') or writeonly,
1634
+ ])
1635
+
1636
+
1637
+ class SoundFileError(Exception):
1638
+ """Base class for all soundfile-specific errors."""
1639
+ pass
1640
+
1641
+ class SoundFileRuntimeError(SoundFileError, RuntimeError):
1642
+ """soundfile module runtime error.
1643
+
1644
+ Errors that used to be `RuntimeError`."""
1645
+ pass
1646
+
1647
+ class LibsndfileError(SoundFileRuntimeError):
1648
+ """libsndfile errors.
1649
+
1650
+
1651
+ Attributes
1652
+ ----------
1653
+ code
1654
+ libsndfile internal error number.
1655
+ """
1656
+ def __init__(self, code, prefix=""):
1657
+ SoundFileRuntimeError.__init__(self, code, prefix)
1658
+ self.code = code
1659
+ self.prefix = prefix
1660
+
1661
+ @property
1662
+ def error_string(self):
1663
+ """Raw libsndfile error message."""
1664
+ if self.code:
1665
+ err_str = _snd.sf_error_number(self.code)
1666
+ return _ffi.string(err_str).decode('utf-8', 'replace')
1667
+ else:
1668
+ # Due to race conditions, if used concurrently, sf_error() may
1669
+ # return 0 (= no error) even if an error has happened.
1670
+ # See https://github.com/erikd/libsndfile/issues/610 for details.
1671
+ return "(Garbled error message from libsndfile)"
1672
+
1673
+ def __str__(self):
1674
+ return self.prefix + self.error_string