-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
/
Copy pathwavfile.py
175 lines (155 loc) · 4.36 KB
/
wavfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
"""
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.
"""
import numpy
import struct
import warnings
class WavFileWarning(UserWarning):
pass
_big_endian = False
# assumes file pointer is immediately
# after the 'fmt ' id
def _read_fmt_chunk(fid):
if _big_endian:
fmt = '>'
else:
fmt = '<'
res = struct.unpack(fmt+'ihHIIHH',fid.read(20))
size, comp, noc, rate, sbytes, ba, bits = res
if (comp != 1 or size > 16):
warnings.warn("Unfamiliar format bytes", WavFileWarning)
if (size>16):
fid.read(size-16)
return size, comp, noc, rate, sbytes, ba, bits
# assumes file pointer is immediately
# after the 'data' id
def _read_data_chunk(fid, noc, bits):
if _big_endian:
fmt = '>i'
else:
fmt = '<i'
size = struct.unpack(fmt,fid.read(4))[0]
if bits == 8:
data = numpy.fromfile(fid, dtype=numpy.ubyte, count=size)
if noc > 1:
data = data.reshape(-1,noc)
else:
bytes = bits//8
if _big_endian:
dtype = '>i%d' % bytes
else:
dtype = '<i%d' % bytes
data = numpy.fromfile(fid, dtype=dtype, count=size//bytes)
if noc > 1:
data = data.reshape(-1,noc)
return data
def _read_riff_chunk(fid):
global _big_endian
str1 = fid.read(4)
if str1 == 'RIFX':
_big_endian = True
elif str1 != 'RIFF':
raise ValueError("Not a WAV file.")
if _big_endian:
fmt = '>I'
else:
fmt = '<I'
fsize = struct.unpack(fmt, fid.read(4))[0] + 8
str2 = fid.read(4)
if (str2 != 'WAVE'):
raise ValueError("Not a WAV file.")
if str1 == 'RIFX':
_big_endian = True
return fsize
# open a wave-file
def read(file):
"""
Return the sample rate (in samples/sec) and data from a WAV file
Parameters
----------
file : file
Input wav file.
Returns
-------
rate : int
Sample rate of wav file
data : numpy array
Data read from wav file
Notes
-----
* The file can be an open file or a filename.
* The returned sample rate is a Python integer
* The data is returned as a numpy array with a
data-type determined from the file.
"""
if hasattr(file,'read'):
fid = file
else:
fid = open(file, 'rb')
fsize = _read_riff_chunk(fid)
noc = 1
bits = 8
while (fid.tell() < fsize):
# read the next chunk
chunk_id = fid.read(4)
if chunk_id == 'fmt ':
size, comp, noc, rate, sbytes, ba, bits = _read_fmt_chunk(fid)
elif chunk_id == 'data':
data = _read_data_chunk(fid, noc, bits)
else:
warnings.warn("chunk not understood", WavFileWarning)
size = struct.unpack('I',fid.read(4))[0]
bytes = fid.read(size)
fid.close()
return rate, data
# Write a wave-file
# sample rate, data
def write(filename, rate, data):
"""
Write a numpy array as a WAV file
Parameters
----------
filename : file
The name of the file to write (will be over-written).
rate : int
The sample rate (in samples/sec).
data : ndarray
A 1-D or 2-D numpy array of integer data-type.
Notes
-----
* Writes a simple uncompressed WAV file.
* The bits-per-sample will be determined by the data-type.
* To write multiple-channels, use a 2-D array of shape
(Nsamples, Nchannels).
"""
fid = open(filename, 'wb')
fid.write('RIFF')
fid.write('\x00\x00\x00\x00')
fid.write('WAVE')
# fmt chunk
fid.write('fmt ')
if data.ndim == 1:
noc = 1
else:
noc = data.shape[1]
bits = data.dtype.itemsize * 8
sbytes = rate*(bits / 8)*noc
ba = noc * (bits / 8)
fid.write(struct.pack('<ihHIIHH', 16, 1, noc, rate, sbytes, ba, bits))
# data chunk
fid.write('data')
fid.write(struct.pack('<i', data.nbytes))
import sys
if data.dtype.byteorder == '>' or (data.dtype.byteorder == '=' and sys.byteorder == 'big'):
data = data.byteswap()
data.tofile(fid)
# Determine file size and place it in correct
# position at start of the file.
size = fid.tell()
fid.seek(4)
fid.write(struct.pack('<i', size-8))
fid.close()