-
Notifications
You must be signed in to change notification settings - Fork 1
/
utility.py
142 lines (116 loc) · 4.2 KB
/
utility.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
"""Helper functions for working with audio files in NumPy."""
import math
import numpy as np
import scipy.signal as ssi
def pcm2float(sig, dtype='float64'):
"""Convert PCM signal to floating point with a range from -1 to 1.
Use dtype='float32' for single precision.
Parameters
----------
sig : array_like
Input array, must have integral type.
dtype : data type, optional
Desired (floating point) data type.
Returns
-------
numpy.ndarray
Normalized floating point data.
See Also
--------
float2pcm, dtype
"""
sig = np.asarray(sig)
if sig.dtype.kind not in 'iu':
raise TypeError("'sig' must be an array of integers")
dtype = np.dtype(dtype)
if dtype.kind != 'f':
raise TypeError("'dtype' must be a floating point type")
i = np.iinfo(sig.dtype)
abs_max = 2 ** (i.bits - 1)
offset = i.min + abs_max
return (sig.astype(dtype) - offset) / abs_max
def float2pcm(sig, dtype='int16'):
"""Convert floating point signal with a range from -1 to 1 to PCM.
Any signal values outside the interval [-1.0, 1.0) are clipped.
No dithering is used.
Note that there are different possibilities for scaling floating
point numbers to PCM numbers, this function implements just one of
them. For an overview of alternatives see
http://blog.bjornroche.com/2009/12/int-float-int-its-jungle-out-there.html
Parameters
----------
sig : array_like
Input array, must have floating point type.
dtype : data type, optional
Desired (integer) data type.
Returns
-------
numpy.ndarray
Integer data, scaled and clipped to the range of the given
*dtype*.
See Also
--------
pcm2float, dtype
"""
sig = np.asarray(sig)
if sig.dtype.kind != 'f':
raise TypeError("'sig' must be a float array")
dtype = np.dtype(dtype)
if dtype.kind not in 'iu':
raise TypeError("'dtype' must be an integer type")
i = np.iinfo(dtype)
abs_max = 2 ** (i.bits - 1)
offset = i.min + abs_max
return (sig * abs_max + offset).clip(i.min, i.max).astype(dtype)
def pcm24to32(data, channels=1, normalize=True):
"""Convert 24-bit PCM data to 32-bit.
Parameters
----------
data : buffer
A buffer object where each group of 3 bytes represents one
little-endian 24-bit value.
channels : int, optional
Number of channels, by default 1.
normalize : bool, optional
If ``True`` (the default) the additional zero-byte is added as
least significant byte, effectively multiplying each value by
256, which leads to the maximum 24-bit value being mapped to the
maximum 32-bit value. If ``False``, the zero-byte is added as
most significant byte and the values are not changed.
Returns
-------
numpy.ndarray
The content of *data* converted to an *int32* array, where each
value was padded with zero-bits in the least significant byte
(``normalize=True``) or in the most significant byte
(``normalize=False``).
"""
if len(data) % 3 != 0:
raise ValueError('Size of data must be a multiple of 3 bytes')
out = np.zeros(len(data) // 3, dtype='<i4')
out.shape = -1, channels
temp = out.view('uint8').reshape(-1, 4)
if normalize:
# write to last 3 columns, leave LSB at zero
columns = slice(1, None)
else:
# write to first 3 columns, leave MSB at zero
columns = slice(None, -1)
temp[:, columns] = np.frombuffer(data, dtype='uint8').reshape(-1, 3)
return out
def calc_valid_power(signal, silence_threshold=1e-5):
valid_signal = signal[abs(signal) > silence_threshold]
if len(valid_signal) == 0:
return np.sum(signal**2) / len(signal)
power = np.sum(valid_signal ** 2) / len(valid_signal)
return power
def convert_samplerate(signal, f_in, f_out):
if f_in != f_out:
newlen = int(math.ceil(len(signal) * f_out / f_in))
signal = ssi.resample(signal, newlen)
return signal
def smart_convolve(a, b):
if len(a) / len(b) > 100 or len(b) / len(a) > 100:
return ssi.oaconvolve(a, b)
else:
return ssi.fftconvolve(a, b)