Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/io/wavfile.py: 80%
478 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
1"""
2Module to read / write wav files using NumPy arrays
4Functions
5---------
6`read`: Return the sample rate (in samples/sec) and data from a WAV file.
8`write`: Write a NumPy array as a WAV file.
10"""
11import io
12import sys
13import numpy
14import struct
15import warnings
16from enum import IntEnum
19__all__ = [
20 'WavFileWarning',
21 'read',
22 'write'
23]
26class WavFileWarning(UserWarning):
27 pass
30class WAVE_FORMAT(IntEnum):
31 """
32 WAVE form wFormatTag IDs
34 Complete list is in mmreg.h in Windows 10 SDK. ALAC and OPUS are the
35 newest additions, in v10.0.14393 2016-07
36 """
37 UNKNOWN = 0x0000
38 PCM = 0x0001
39 ADPCM = 0x0002
40 IEEE_FLOAT = 0x0003
41 VSELP = 0x0004
42 IBM_CVSD = 0x0005
43 ALAW = 0x0006
44 MULAW = 0x0007
45 DTS = 0x0008
46 DRM = 0x0009
47 WMAVOICE9 = 0x000A
48 WMAVOICE10 = 0x000B
49 OKI_ADPCM = 0x0010
50 DVI_ADPCM = 0x0011
51 IMA_ADPCM = 0x0011 # Duplicate
52 MEDIASPACE_ADPCM = 0x0012
53 SIERRA_ADPCM = 0x0013
54 G723_ADPCM = 0x0014
55 DIGISTD = 0x0015
56 DIGIFIX = 0x0016
57 DIALOGIC_OKI_ADPCM = 0x0017
58 MEDIAVISION_ADPCM = 0x0018
59 CU_CODEC = 0x0019
60 HP_DYN_VOICE = 0x001A
61 YAMAHA_ADPCM = 0x0020
62 SONARC = 0x0021
63 DSPGROUP_TRUESPEECH = 0x0022
64 ECHOSC1 = 0x0023
65 AUDIOFILE_AF36 = 0x0024
66 APTX = 0x0025
67 AUDIOFILE_AF10 = 0x0026
68 PROSODY_1612 = 0x0027
69 LRC = 0x0028
70 DOLBY_AC2 = 0x0030
71 GSM610 = 0x0031
72 MSNAUDIO = 0x0032
73 ANTEX_ADPCME = 0x0033
74 CONTROL_RES_VQLPC = 0x0034
75 DIGIREAL = 0x0035
76 DIGIADPCM = 0x0036
77 CONTROL_RES_CR10 = 0x0037
78 NMS_VBXADPCM = 0x0038
79 CS_IMAADPCM = 0x0039
80 ECHOSC3 = 0x003A
81 ROCKWELL_ADPCM = 0x003B
82 ROCKWELL_DIGITALK = 0x003C
83 XEBEC = 0x003D
84 G721_ADPCM = 0x0040
85 G728_CELP = 0x0041
86 MSG723 = 0x0042
87 INTEL_G723_1 = 0x0043
88 INTEL_G729 = 0x0044
89 SHARP_G726 = 0x0045
90 MPEG = 0x0050
91 RT24 = 0x0052
92 PAC = 0x0053
93 MPEGLAYER3 = 0x0055
94 LUCENT_G723 = 0x0059
95 CIRRUS = 0x0060
96 ESPCM = 0x0061
97 VOXWARE = 0x0062
98 CANOPUS_ATRAC = 0x0063
99 G726_ADPCM = 0x0064
100 G722_ADPCM = 0x0065
101 DSAT = 0x0066
102 DSAT_DISPLAY = 0x0067
103 VOXWARE_BYTE_ALIGNED = 0x0069
104 VOXWARE_AC8 = 0x0070
105 VOXWARE_AC10 = 0x0071
106 VOXWARE_AC16 = 0x0072
107 VOXWARE_AC20 = 0x0073
108 VOXWARE_RT24 = 0x0074
109 VOXWARE_RT29 = 0x0075
110 VOXWARE_RT29HW = 0x0076
111 VOXWARE_VR12 = 0x0077
112 VOXWARE_VR18 = 0x0078
113 VOXWARE_TQ40 = 0x0079
114 VOXWARE_SC3 = 0x007A
115 VOXWARE_SC3_1 = 0x007B
116 SOFTSOUND = 0x0080
117 VOXWARE_TQ60 = 0x0081
118 MSRT24 = 0x0082
119 G729A = 0x0083
120 MVI_MVI2 = 0x0084
121 DF_G726 = 0x0085
122 DF_GSM610 = 0x0086
123 ISIAUDIO = 0x0088
124 ONLIVE = 0x0089
125 MULTITUDE_FT_SX20 = 0x008A
126 INFOCOM_ITS_G721_ADPCM = 0x008B
127 CONVEDIA_G729 = 0x008C
128 CONGRUENCY = 0x008D
129 SBC24 = 0x0091
130 DOLBY_AC3_SPDIF = 0x0092
131 MEDIASONIC_G723 = 0x0093
132 PROSODY_8KBPS = 0x0094
133 ZYXEL_ADPCM = 0x0097
134 PHILIPS_LPCBB = 0x0098
135 PACKED = 0x0099
136 MALDEN_PHONYTALK = 0x00A0
137 RACAL_RECORDER_GSM = 0x00A1
138 RACAL_RECORDER_G720_A = 0x00A2
139 RACAL_RECORDER_G723_1 = 0x00A3
140 RACAL_RECORDER_TETRA_ACELP = 0x00A4
141 NEC_AAC = 0x00B0
142 RAW_AAC1 = 0x00FF
143 RHETOREX_ADPCM = 0x0100
144 IRAT = 0x0101
145 VIVO_G723 = 0x0111
146 VIVO_SIREN = 0x0112
147 PHILIPS_CELP = 0x0120
148 PHILIPS_GRUNDIG = 0x0121
149 DIGITAL_G723 = 0x0123
150 SANYO_LD_ADPCM = 0x0125
151 SIPROLAB_ACEPLNET = 0x0130
152 SIPROLAB_ACELP4800 = 0x0131
153 SIPROLAB_ACELP8V3 = 0x0132
154 SIPROLAB_G729 = 0x0133
155 SIPROLAB_G729A = 0x0134
156 SIPROLAB_KELVIN = 0x0135
157 VOICEAGE_AMR = 0x0136
158 G726ADPCM = 0x0140
159 DICTAPHONE_CELP68 = 0x0141
160 DICTAPHONE_CELP54 = 0x0142
161 QUALCOMM_PUREVOICE = 0x0150
162 QUALCOMM_HALFRATE = 0x0151
163 TUBGSM = 0x0155
164 MSAUDIO1 = 0x0160
165 WMAUDIO2 = 0x0161
166 WMAUDIO3 = 0x0162
167 WMAUDIO_LOSSLESS = 0x0163
168 WMASPDIF = 0x0164
169 UNISYS_NAP_ADPCM = 0x0170
170 UNISYS_NAP_ULAW = 0x0171
171 UNISYS_NAP_ALAW = 0x0172
172 UNISYS_NAP_16K = 0x0173
173 SYCOM_ACM_SYC008 = 0x0174
174 SYCOM_ACM_SYC701_G726L = 0x0175
175 SYCOM_ACM_SYC701_CELP54 = 0x0176
176 SYCOM_ACM_SYC701_CELP68 = 0x0177
177 KNOWLEDGE_ADVENTURE_ADPCM = 0x0178
178 FRAUNHOFER_IIS_MPEG2_AAC = 0x0180
179 DTS_DS = 0x0190
180 CREATIVE_ADPCM = 0x0200
181 CREATIVE_FASTSPEECH8 = 0x0202
182 CREATIVE_FASTSPEECH10 = 0x0203
183 UHER_ADPCM = 0x0210
184 ULEAD_DV_AUDIO = 0x0215
185 ULEAD_DV_AUDIO_1 = 0x0216
186 QUARTERDECK = 0x0220
187 ILINK_VC = 0x0230
188 RAW_SPORT = 0x0240
189 ESST_AC3 = 0x0241
190 GENERIC_PASSTHRU = 0x0249
191 IPI_HSX = 0x0250
192 IPI_RPELP = 0x0251
193 CS2 = 0x0260
194 SONY_SCX = 0x0270
195 SONY_SCY = 0x0271
196 SONY_ATRAC3 = 0x0272
197 SONY_SPC = 0x0273
198 TELUM_AUDIO = 0x0280
199 TELUM_IA_AUDIO = 0x0281
200 NORCOM_VOICE_SYSTEMS_ADPCM = 0x0285
201 FM_TOWNS_SND = 0x0300
202 MICRONAS = 0x0350
203 MICRONAS_CELP833 = 0x0351
204 BTV_DIGITAL = 0x0400
205 INTEL_MUSIC_CODER = 0x0401
206 INDEO_AUDIO = 0x0402
207 QDESIGN_MUSIC = 0x0450
208 ON2_VP7_AUDIO = 0x0500
209 ON2_VP6_AUDIO = 0x0501
210 VME_VMPCM = 0x0680
211 TPC = 0x0681
212 LIGHTWAVE_LOSSLESS = 0x08AE
213 OLIGSM = 0x1000
214 OLIADPCM = 0x1001
215 OLICELP = 0x1002
216 OLISBC = 0x1003
217 OLIOPR = 0x1004
218 LH_CODEC = 0x1100
219 LH_CODEC_CELP = 0x1101
220 LH_CODEC_SBC8 = 0x1102
221 LH_CODEC_SBC12 = 0x1103
222 LH_CODEC_SBC16 = 0x1104
223 NORRIS = 0x1400
224 ISIAUDIO_2 = 0x1401
225 SOUNDSPACE_MUSICOMPRESS = 0x1500
226 MPEG_ADTS_AAC = 0x1600
227 MPEG_RAW_AAC = 0x1601
228 MPEG_LOAS = 0x1602
229 NOKIA_MPEG_ADTS_AAC = 0x1608
230 NOKIA_MPEG_RAW_AAC = 0x1609
231 VODAFONE_MPEG_ADTS_AAC = 0x160A
232 VODAFONE_MPEG_RAW_AAC = 0x160B
233 MPEG_HEAAC = 0x1610
234 VOXWARE_RT24_SPEECH = 0x181C
235 SONICFOUNDRY_LOSSLESS = 0x1971
236 INNINGS_TELECOM_ADPCM = 0x1979
237 LUCENT_SX8300P = 0x1C07
238 LUCENT_SX5363S = 0x1C0C
239 CUSEEME = 0x1F03
240 NTCSOFT_ALF2CM_ACM = 0x1FC4
241 DVM = 0x2000
242 DTS2 = 0x2001
243 MAKEAVIS = 0x3313
244 DIVIO_MPEG4_AAC = 0x4143
245 NOKIA_ADAPTIVE_MULTIRATE = 0x4201
246 DIVIO_G726 = 0x4243
247 LEAD_SPEECH = 0x434C
248 LEAD_VORBIS = 0x564C
249 WAVPACK_AUDIO = 0x5756
250 OGG_VORBIS_MODE_1 = 0x674F
251 OGG_VORBIS_MODE_2 = 0x6750
252 OGG_VORBIS_MODE_3 = 0x6751
253 OGG_VORBIS_MODE_1_PLUS = 0x676F
254 OGG_VORBIS_MODE_2_PLUS = 0x6770
255 OGG_VORBIS_MODE_3_PLUS = 0x6771
256 ALAC = 0x6C61
257 _3COM_NBX = 0x7000 # Can't have leading digit
258 OPUS = 0x704F
259 FAAD_AAC = 0x706D
260 AMR_NB = 0x7361
261 AMR_WB = 0x7362
262 AMR_WP = 0x7363
263 GSM_AMR_CBR = 0x7A21
264 GSM_AMR_VBR_SID = 0x7A22
265 COMVERSE_INFOSYS_G723_1 = 0xA100
266 COMVERSE_INFOSYS_AVQSBC = 0xA101
267 COMVERSE_INFOSYS_SBC = 0xA102
268 SYMBOL_G729_A = 0xA103
269 VOICEAGE_AMR_WB = 0xA104
270 INGENIENT_G726 = 0xA105
271 MPEG4_AAC = 0xA106
272 ENCORE_G726 = 0xA107
273 ZOLL_ASAO = 0xA108
274 SPEEX_VOICE = 0xA109
275 VIANIX_MASC = 0xA10A
276 WM9_SPECTRUM_ANALYZER = 0xA10B
277 WMF_SPECTRUM_ANAYZER = 0xA10C
278 GSM_610 = 0xA10D
279 GSM_620 = 0xA10E
280 GSM_660 = 0xA10F
281 GSM_690 = 0xA110
282 GSM_ADAPTIVE_MULTIRATE_WB = 0xA111
283 POLYCOM_G722 = 0xA112
284 POLYCOM_G728 = 0xA113
285 POLYCOM_G729_A = 0xA114
286 POLYCOM_SIREN = 0xA115
287 GLOBAL_IP_ILBC = 0xA116
288 RADIOTIME_TIME_SHIFT_RADIO = 0xA117
289 NICE_ACA = 0xA118
290 NICE_ADPCM = 0xA119
291 VOCORD_G721 = 0xA11A
292 VOCORD_G726 = 0xA11B
293 VOCORD_G722_1 = 0xA11C
294 VOCORD_G728 = 0xA11D
295 VOCORD_G729 = 0xA11E
296 VOCORD_G729_A = 0xA11F
297 VOCORD_G723_1 = 0xA120
298 VOCORD_LBC = 0xA121
299 NICE_G728 = 0xA122
300 FRACE_TELECOM_G729 = 0xA123
301 CODIAN = 0xA124
302 FLAC = 0xF1AC
303 EXTENSIBLE = 0xFFFE
304 DEVELOPMENT = 0xFFFF
307KNOWN_WAVE_FORMATS = {WAVE_FORMAT.PCM, WAVE_FORMAT.IEEE_FLOAT}
310def _raise_bad_format(format_tag):
311 try:
312 format_name = WAVE_FORMAT(format_tag).name
313 except ValueError:
314 format_name = f'{format_tag:#06x}'
315 raise ValueError(f"Unknown wave file format: {format_name}. Supported "
316 "formats: " +
317 ', '.join(x.name for x in KNOWN_WAVE_FORMATS))
320def _read_fmt_chunk(fid, is_big_endian):
321 """
322 Returns
323 -------
324 size : int
325 size of format subchunk in bytes (minus 8 for "fmt " and itself)
326 format_tag : int
327 PCM, float, or compressed format
328 channels : int
329 number of channels
330 fs : int
331 sampling frequency in samples per second
332 bytes_per_second : int
333 overall byte rate for the file
334 block_align : int
335 bytes per sample, including all channels
336 bit_depth : int
337 bits per sample
339 Notes
340 -----
341 Assumes file pointer is immediately after the 'fmt ' id
342 """
343 if is_big_endian:
344 fmt = '>'
345 else:
346 fmt = '<'
348 size = struct.unpack(fmt+'I', fid.read(4))[0]
350 if size < 16:
351 raise ValueError("Binary structure of wave file is not compliant")
353 res = struct.unpack(fmt+'HHIIHH', fid.read(16))
354 bytes_read = 16
356 format_tag, channels, fs, bytes_per_second, block_align, bit_depth = res
358 if format_tag == WAVE_FORMAT.EXTENSIBLE and size >= (16+2):
359 ext_chunk_size = struct.unpack(fmt+'H', fid.read(2))[0]
360 bytes_read += 2
361 if ext_chunk_size >= 22:
362 extensible_chunk_data = fid.read(22)
363 bytes_read += 22
364 raw_guid = extensible_chunk_data[2+4:2+4+16]
365 # GUID template {XXXXXXXX-0000-0010-8000-00AA00389B71} (RFC-2361)
366 # MS GUID byte order: first three groups are native byte order,
367 # rest is Big Endian
368 if is_big_endian:
369 tail = b'\x00\x00\x00\x10\x80\x00\x00\xAA\x00\x38\x9B\x71'
370 else:
371 tail = b'\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71'
372 if raw_guid.endswith(tail):
373 format_tag = struct.unpack(fmt+'I', raw_guid[:4])[0]
374 else:
375 raise ValueError("Binary structure of wave file is not compliant")
377 if format_tag not in KNOWN_WAVE_FORMATS:
378 _raise_bad_format(format_tag)
380 # move file pointer to next chunk
381 if size > bytes_read:
382 fid.read(size - bytes_read)
384 # fmt should always be 16, 18 or 40, but handle it just in case
385 _handle_pad_byte(fid, size)
387 if format_tag == WAVE_FORMAT.PCM:
388 if bytes_per_second != fs * block_align:
389 raise ValueError("WAV header is invalid: nAvgBytesPerSec must"
390 " equal product of nSamplesPerSec and"
391 " nBlockAlign, but file has nSamplesPerSec ="
392 f" {fs}, nBlockAlign = {block_align}, and"
393 f" nAvgBytesPerSec = {bytes_per_second}")
395 return (size, format_tag, channels, fs, bytes_per_second, block_align,
396 bit_depth)
399def _read_data_chunk(fid, format_tag, channels, bit_depth, is_big_endian,
400 block_align, mmap=False):
401 """
402 Notes
403 -----
404 Assumes file pointer is immediately after the 'data' id
406 It's possible to not use all available bits in a container, or to store
407 samples in a container bigger than necessary, so bytes_per_sample uses
408 the actual reported container size (nBlockAlign / nChannels). Real-world
409 examples:
411 Adobe Audition's "24-bit packed int (type 1, 20-bit)"
413 nChannels = 2, nBlockAlign = 6, wBitsPerSample = 20
415 http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples/AFsp/M1F1-int12-AFsp.wav
416 is:
418 nChannels = 2, nBlockAlign = 4, wBitsPerSample = 12
420 http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/multichaudP.pdf
421 gives an example of:
423 nChannels = 2, nBlockAlign = 8, wBitsPerSample = 20
424 """
425 if is_big_endian:
426 fmt = '>'
427 else:
428 fmt = '<'
430 # Size of the data subchunk in bytes
431 size = struct.unpack(fmt+'I', fid.read(4))[0]
433 # Number of bytes per sample (sample container size)
434 bytes_per_sample = block_align // channels
435 n_samples = size // bytes_per_sample
437 if format_tag == WAVE_FORMAT.PCM:
438 if 1 <= bit_depth <= 8:
439 dtype = 'u1' # WAV of 8-bit integer or less are unsigned
440 elif bytes_per_sample in {3, 5, 6, 7}:
441 # No compatible dtype. Load as raw bytes for reshaping later.
442 dtype = 'V1'
443 elif bit_depth <= 64:
444 # Remaining bit depths can map directly to signed numpy dtypes
445 dtype = f'{fmt}i{bytes_per_sample}'
446 else:
447 raise ValueError("Unsupported bit depth: the WAV file "
448 f"has {bit_depth}-bit integer data.")
449 elif format_tag == WAVE_FORMAT.IEEE_FLOAT:
450 if bit_depth in {32, 64}:
451 dtype = f'{fmt}f{bytes_per_sample}'
452 else:
453 raise ValueError("Unsupported bit depth: the WAV file "
454 f"has {bit_depth}-bit floating-point data.")
455 else:
456 _raise_bad_format(format_tag)
458 start = fid.tell()
459 if not mmap:
460 try:
461 count = size if dtype == 'V1' else n_samples
462 data = numpy.fromfile(fid, dtype=dtype, count=count)
463 except io.UnsupportedOperation: # not a C-like file
464 fid.seek(start, 0) # just in case it seeked, though it shouldn't
465 data = numpy.frombuffer(fid.read(size), dtype=dtype)
467 if dtype == 'V1':
468 # Rearrange raw bytes into smallest compatible numpy dtype
469 dt = f'{fmt}i4' if bytes_per_sample == 3 else f'{fmt}i8'
470 a = numpy.zeros((len(data) // bytes_per_sample, numpy.dtype(dt).itemsize),
471 dtype='V1')
472 if is_big_endian:
473 a[:, :bytes_per_sample] = data.reshape((-1, bytes_per_sample))
474 else:
475 a[:, -bytes_per_sample:] = data.reshape((-1, bytes_per_sample))
476 data = a.view(dt).reshape(a.shape[:-1])
477 else:
478 if bytes_per_sample in {1, 2, 4, 8}:
479 start = fid.tell()
480 data = numpy.memmap(fid, dtype=dtype, mode='c', offset=start,
481 shape=(n_samples,))
482 fid.seek(start + size)
483 else:
484 raise ValueError("mmap=True not compatible with "
485 f"{bytes_per_sample}-byte container size.")
487 _handle_pad_byte(fid, size)
489 if channels > 1:
490 data = data.reshape(-1, channels)
491 return data
494def _skip_unknown_chunk(fid, is_big_endian):
495 if is_big_endian:
496 fmt = '>I'
497 else:
498 fmt = '<I'
500 data = fid.read(4)
501 # call unpack() and seek() only if we have really read data from file
502 # otherwise empty read at the end of the file would trigger
503 # unnecessary exception at unpack() call
504 # in case data equals somehow to 0, there is no need for seek() anyway
505 if data:
506 size = struct.unpack(fmt, data)[0]
507 fid.seek(size, 1)
508 _handle_pad_byte(fid, size)
511def _read_riff_chunk(fid):
512 str1 = fid.read(4) # File signature
513 if str1 == b'RIFF':
514 is_big_endian = False
515 fmt = '<I'
516 elif str1 == b'RIFX':
517 is_big_endian = True
518 fmt = '>I'
519 else:
520 # There are also .wav files with "FFIR" or "XFIR" signatures?
521 raise ValueError(f"File format {repr(str1)} not understood. Only "
522 "'RIFF' and 'RIFX' supported.")
524 # Size of entire file
525 file_size = struct.unpack(fmt, fid.read(4))[0] + 8
527 str2 = fid.read(4)
528 if str2 != b'WAVE':
529 raise ValueError(f"Not a WAV file. RIFF form type is {repr(str2)}.")
531 return file_size, is_big_endian
534def _handle_pad_byte(fid, size):
535 # "If the chunk size is an odd number of bytes, a pad byte with value zero
536 # is written after ckData." So we need to seek past this after each chunk.
537 if size % 2:
538 fid.seek(1, 1)
541def read(filename, mmap=False):
542 """
543 Open a WAV file.
545 Return the sample rate (in samples/sec) and data from an LPCM WAV file.
547 Parameters
548 ----------
549 filename : string or open file handle
550 Input WAV file.
551 mmap : bool, optional
552 Whether to read data as memory-mapped (default: False). Not compatible
553 with some bit depths; see Notes. Only to be used on real files.
555 .. versionadded:: 0.12.0
557 Returns
558 -------
559 rate : int
560 Sample rate of WAV file.
561 data : numpy array
562 Data read from WAV file. Data-type is determined from the file;
563 see Notes. Data is 1-D for 1-channel WAV, or 2-D of shape
564 (Nsamples, Nchannels) otherwise. If a file-like input without a
565 C-like file descriptor (e.g., :class:`python:io.BytesIO`) is
566 passed, this will not be writeable.
568 Notes
569 -----
570 Common data types: [1]_
572 ===================== =========== =========== =============
573 WAV format Min Max NumPy dtype
574 ===================== =========== =========== =============
575 32-bit floating-point -1.0 +1.0 float32
576 32-bit integer PCM -2147483648 +2147483647 int32
577 24-bit integer PCM -2147483648 +2147483392 int32
578 16-bit integer PCM -32768 +32767 int16
579 8-bit integer PCM 0 255 uint8
580 ===================== =========== =========== =============
582 WAV files can specify arbitrary bit depth, and this function supports
583 reading any integer PCM depth from 1 to 64 bits. Data is returned in the
584 smallest compatible numpy int type, in left-justified format. 8-bit and
585 lower is unsigned, while 9-bit and higher is signed.
587 For example, 24-bit data will be stored as int32, with the MSB of the
588 24-bit data stored at the MSB of the int32, and typically the least
589 significant byte is 0x00. (However, if a file actually contains data past
590 its specified bit depth, those bits will be read and output, too. [2]_)
592 This bit justification and sign matches WAV's native internal format, which
593 allows memory mapping of WAV files that use 1, 2, 4, or 8 bytes per sample
594 (so 24-bit files cannot be memory-mapped, but 32-bit can).
596 IEEE float PCM in 32- or 64-bit format is supported, with or without mmap.
597 Values exceeding [-1, +1] are not clipped.
599 Non-linear PCM (mu-law, A-law) is not supported.
601 References
602 ----------
603 .. [1] IBM Corporation and Microsoft Corporation, "Multimedia Programming
604 Interface and Data Specifications 1.0", section "Data Format of the
605 Samples", August 1991
606 http://www.tactilemedia.com/info/MCI_Control_Info.html
607 .. [2] Adobe Systems Incorporated, "Adobe Audition 3 User Guide", section
608 "Audio file formats: 24-bit Packed Int (type 1, 20-bit)", 2007
610 Examples
611 --------
612 >>> from os.path import dirname, join as pjoin
613 >>> from scipy.io import wavfile
614 >>> import scipy.io
616 Get the filename for an example .wav file from the tests/data directory.
618 >>> data_dir = pjoin(dirname(scipy.io.__file__), 'tests', 'data')
619 >>> wav_fname = pjoin(data_dir, 'test-44100Hz-2ch-32bit-float-be.wav')
621 Load the .wav file contents.
623 >>> samplerate, data = wavfile.read(wav_fname)
624 >>> print(f"number of channels = {data.shape[1]}")
625 number of channels = 2
626 >>> length = data.shape[0] / samplerate
627 >>> print(f"length = {length}s")
628 length = 0.01s
630 Plot the waveform.
632 >>> import matplotlib.pyplot as plt
633 >>> import numpy as np
634 >>> time = np.linspace(0., length, data.shape[0])
635 >>> plt.plot(time, data[:, 0], label="Left channel")
636 >>> plt.plot(time, data[:, 1], label="Right channel")
637 >>> plt.legend()
638 >>> plt.xlabel("Time [s]")
639 >>> plt.ylabel("Amplitude")
640 >>> plt.show()
642 """
643 if hasattr(filename, 'read'):
644 fid = filename
645 mmap = False
646 else:
647 fid = open(filename, 'rb')
649 try:
650 file_size, is_big_endian = _read_riff_chunk(fid)
651 fmt_chunk_received = False
652 data_chunk_received = False
653 while fid.tell() < file_size:
654 # read the next chunk
655 chunk_id = fid.read(4)
657 if not chunk_id:
658 if data_chunk_received:
659 # End of file but data successfully read
660 warnings.warn(
661 "Reached EOF prematurely; finished at {:d} bytes, "
662 "expected {:d} bytes from header."
663 .format(fid.tell(), file_size),
664 WavFileWarning, stacklevel=2)
665 break
666 else:
667 raise ValueError("Unexpected end of file.")
668 elif len(chunk_id) < 4:
669 msg = f"Incomplete chunk ID: {repr(chunk_id)}"
670 # If we have the data, ignore the broken chunk
671 if fmt_chunk_received and data_chunk_received:
672 warnings.warn(msg + ", ignoring it.", WavFileWarning,
673 stacklevel=2)
674 else:
675 raise ValueError(msg)
677 if chunk_id == b'fmt ':
678 fmt_chunk_received = True
679 fmt_chunk = _read_fmt_chunk(fid, is_big_endian)
680 format_tag, channels, fs = fmt_chunk[1:4]
681 bit_depth = fmt_chunk[6]
682 block_align = fmt_chunk[5]
683 elif chunk_id == b'fact':
684 _skip_unknown_chunk(fid, is_big_endian)
685 elif chunk_id == b'data':
686 data_chunk_received = True
687 if not fmt_chunk_received:
688 raise ValueError("No fmt chunk before data")
689 data = _read_data_chunk(fid, format_tag, channels, bit_depth,
690 is_big_endian, block_align, mmap)
691 elif chunk_id == b'LIST':
692 # Someday this could be handled properly but for now skip it
693 _skip_unknown_chunk(fid, is_big_endian)
694 elif chunk_id in {b'JUNK', b'Fake'}:
695 # Skip alignment chunks without warning
696 _skip_unknown_chunk(fid, is_big_endian)
697 else:
698 warnings.warn("Chunk (non-data) not understood, skipping it.",
699 WavFileWarning, stacklevel=2)
700 _skip_unknown_chunk(fid, is_big_endian)
701 finally:
702 if not hasattr(filename, 'read'):
703 fid.close()
704 else:
705 fid.seek(0)
707 return fs, data
710def write(filename, rate, data):
711 """
712 Write a NumPy array as a WAV file.
714 Parameters
715 ----------
716 filename : string or open file handle
717 Output wav file.
718 rate : int
719 The sample rate (in samples/sec).
720 data : ndarray
721 A 1-D or 2-D NumPy array of either integer or float data-type.
723 Notes
724 -----
725 * Writes a simple uncompressed WAV file.
726 * To write multiple-channels, use a 2-D array of shape
727 (Nsamples, Nchannels).
728 * The bits-per-sample and PCM/float will be determined by the data-type.
730 Common data types: [1]_
732 ===================== =========== =========== =============
733 WAV format Min Max NumPy dtype
734 ===================== =========== =========== =============
735 32-bit floating-point -1.0 +1.0 float32
736 32-bit PCM -2147483648 +2147483647 int32
737 16-bit PCM -32768 +32767 int16
738 8-bit PCM 0 255 uint8
739 ===================== =========== =========== =============
741 Note that 8-bit PCM is unsigned.
743 References
744 ----------
745 .. [1] IBM Corporation and Microsoft Corporation, "Multimedia Programming
746 Interface and Data Specifications 1.0", section "Data Format of the
747 Samples", August 1991
748 http://www.tactilemedia.com/info/MCI_Control_Info.html
750 Examples
751 --------
752 Create a 100Hz sine wave, sampled at 44100Hz.
753 Write to 16-bit PCM, Mono.
755 >>> from scipy.io.wavfile import write
756 >>> import numpy as np
757 >>> samplerate = 44100; fs = 100
758 >>> t = np.linspace(0., 1., samplerate)
759 >>> amplitude = np.iinfo(np.int16).max
760 >>> data = amplitude * np.sin(2. * np.pi * fs * t)
761 >>> write("example.wav", samplerate, data.astype(np.int16))
763 """
764 if hasattr(filename, 'write'):
765 fid = filename
766 else:
767 fid = open(filename, 'wb')
769 fs = rate
771 try:
772 dkind = data.dtype.kind
773 if not (dkind == 'i' or dkind == 'f' or (dkind == 'u' and
774 data.dtype.itemsize == 1)):
775 raise ValueError("Unsupported data type '%s'" % data.dtype)
777 header_data = b''
779 header_data += b'RIFF'
780 header_data += b'\x00\x00\x00\x00'
781 header_data += b'WAVE'
783 # fmt chunk
784 header_data += b'fmt '
785 if dkind == 'f':
786 format_tag = WAVE_FORMAT.IEEE_FLOAT
787 else:
788 format_tag = WAVE_FORMAT.PCM
789 if data.ndim == 1:
790 channels = 1
791 else:
792 channels = data.shape[1]
793 bit_depth = data.dtype.itemsize * 8
794 bytes_per_second = fs*(bit_depth // 8)*channels
795 block_align = channels * (bit_depth // 8)
797 fmt_chunk_data = struct.pack('<HHIIHH', format_tag, channels, fs,
798 bytes_per_second, block_align, bit_depth)
799 if not (dkind == 'i' or dkind == 'u'):
800 # add cbSize field for non-PCM files
801 fmt_chunk_data += b'\x00\x00'
803 header_data += struct.pack('<I', len(fmt_chunk_data))
804 header_data += fmt_chunk_data
806 # fact chunk (non-PCM files)
807 if not (dkind == 'i' or dkind == 'u'):
808 header_data += b'fact'
809 header_data += struct.pack('<II', 4, data.shape[0])
811 # check data size (needs to be immediately before the data chunk)
812 if ((len(header_data)-4-4) + (4+4+data.nbytes)) > 0xFFFFFFFF:
813 raise ValueError("Data exceeds wave file size limit")
815 fid.write(header_data)
817 # data chunk
818 fid.write(b'data')
819 fid.write(struct.pack('<I', data.nbytes))
820 if data.dtype.byteorder == '>' or (data.dtype.byteorder == '=' and
821 sys.byteorder == 'big'):
822 data = data.byteswap()
823 _array_tofile(fid, data)
825 # Determine file size and place it in correct
826 # position at start of the file.
827 size = fid.tell()
828 fid.seek(4)
829 fid.write(struct.pack('<I', size-8))
831 finally:
832 if not hasattr(filename, 'write'):
833 fid.close()
834 else:
835 fid.seek(0)
838def _array_tofile(fid, data):
839 # ravel gives a c-contiguous buffer
840 fid.write(data.ravel().view('b').data)