""" Module to read / write wav files using numpy arrays Functions --------- `read`: Return the sample rate (in samples/sec) and data from a WAV file. `write`: Write a numpy array as a WAV file. """ from __future__ import division, print_function, absolute_import import sys import numpy import struct import warnings __all__ = [ 'WavFileWarning', 'read', 'write' ] class WavFileWarning(UserWarning): pass WAVE_FORMAT_PCM = 0x0001 WAVE_FORMAT_IEEE_FLOAT = 0x0003 WAVE_FORMAT_EXTENSIBLE = 0xfffe KNOWN_WAVE_FORMATS = (WAVE_FORMAT_PCM, WAVE_FORMAT_IEEE_FLOAT) # assumes file pointer is immediately # after the 'fmt ' id def _read_fmt_chunk(fid, is_big_endian): """ Returns ------- size : int size of format subchunk in bytes (minus 8 for "fmt " and itself) format_tag : int PCM, float, or compressed format channels : int number of channels fs : int sampling frequency in samples per second bytes_per_second : int overall byte rate for the file block_align : int bytes per sample, including all channels bit_depth : int bits per sample """ if is_big_endian: fmt = '>' else: fmt = '<' size = res = struct.unpack(fmt+'I', fid.read(4))[0] bytes_read = 0 if size < 16: raise ValueError("Binary structure of wave file is not compliant") res = struct.unpack(fmt+'HHIIHH', fid.read(16)) bytes_read += 16 format_tag, channels, fs, bytes_per_second, block_align, bit_depth = res if format_tag == WAVE_FORMAT_EXTENSIBLE and size >= (16+2): ext_chunk_size = struct.unpack(fmt+'H', fid.read(2))[0] bytes_read += 2 if ext_chunk_size >= 22: extensible_chunk_data = fid.read(22) bytes_read += 22 raw_guid = extensible_chunk_data[2+4:2+4+16] # GUID template {XXXXXXXX-0000-0010-8000-00AA00389B71} (RFC-2361) # MS GUID byte order: first three groups are native byte order, # rest is Big Endian if is_big_endian: tail = b'\x00\x00\x00\x10\x80\x00\x00\xAA\x00\x38\x9B\x71' else: tail = b'\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71' if raw_guid.endswith(tail): format_tag = struct.unpack(fmt+'I', raw_guid[:4])[0] else: raise ValueError("Binary structure of wave file is not compliant") if format_tag not in KNOWN_WAVE_FORMATS: raise ValueError("Unknown wave file format") # move file pointer to next chunk if size > (bytes_read): fid.read(size - bytes_read) return (size, format_tag, channels, fs, bytes_per_second, block_align, bit_depth) # assumes file pointer is immediately after the 'data' id def _read_data_chunk(fid, format_tag, channels, bit_depth, is_big_endian, mmap=False): if is_big_endian: fmt = '>I' else: fmt = ' 1: data = data.reshape(-1, channels) return data def _skip_unknown_chunk(fid, is_big_endian): if is_big_endian: fmt = '>I' else: fmt = ' 0xFFFFFFFF: raise ValueError("Data exceeds wave file size limit") fid.write(header_data) # data chunk fid.write(b'data') fid.write(struct.pack('' or (data.dtype.byteorder == '=' and sys.byteorder == 'big'): data = data.byteswap() _array_tofile(fid, data) # Determine file size and place it in correct # position at start of the file. size = fid.tell() fid.seek(4) fid.write(struct.pack('= 3: def _array_tofile(fid, data): # ravel gives a c-contiguous buffer fid.write(data.ravel().view('b').data) else: def _array_tofile(fid, data): fid.write(data.tostring())