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