91 lines
2.2 KiB
Python
91 lines
2.2 KiB
Python
import soundfile as sf
|
|
import numpy as np
|
|
import json
|
|
|
|
def read(fname, v=False, **kwargs):
|
|
"""
|
|
Read a sound file from its filename. If there is no comment field in the file's
|
|
metadata, it will silently not scale the file.
|
|
|
|
Parameters
|
|
----------
|
|
fname : str
|
|
The filename.
|
|
v : bool, default=False
|
|
Whether to return a boolean indicating whether the file was scaled.
|
|
**kwargs :
|
|
Keyword arguements as passed to :fun:`sounfile.read`.
|
|
|
|
Returns
|
|
-------
|
|
x : ndarray
|
|
fs : float, int
|
|
scaled : bool
|
|
If `v` is True, whether the file was scaled or not.
|
|
"""
|
|
|
|
kwargs_read = {k:v for k,v in kwargs.items() if k in ['frames', 'fill_value', 'atleast_2d', 'dtype']}
|
|
kwargs_sf = {k:v for k,v in kwargs.items() if k in ['samplerate', 'channels', 'subtype', 'endian', 'format']}
|
|
|
|
if 'out' in kwargs:
|
|
raise ValueError("The `out` arguement cannot be used.")
|
|
|
|
with sf.SoundFile(fname, *kwargs_sf) as f:
|
|
x = f.read()
|
|
fs = f.samplerate
|
|
try:
|
|
cmt = json.loads(f.comment)
|
|
r = cmt['rms']
|
|
except:
|
|
r = 1
|
|
scaled = False
|
|
if r != 1:
|
|
x = x/rms(x) * r
|
|
scaled = True
|
|
if v:
|
|
return x, fs, scaled
|
|
else:
|
|
return x, fs
|
|
|
|
def rms(x):
|
|
return np.sqrt(np.mean(x**2))
|
|
|
|
def write(fname, x, fs, *args, **kwargs):
|
|
"""
|
|
Write an array to a sound file with maximum scale.
|
|
This only works with WAV files. Note that the extra arguments
|
|
`args` and `kwargs` will be ignored.
|
|
|
|
Parameters
|
|
----------
|
|
x : ndarray
|
|
The sound array.
|
|
fs : float, int
|
|
The sampling rate.
|
|
fname : str
|
|
The filename.
|
|
"""
|
|
r = rms(x)
|
|
y = np.atleast_2d(x / np.max(np.abs(x)) * .98)
|
|
|
|
if y.shape[1]>y.shape[0]:
|
|
y = y.T
|
|
|
|
with sf.SoundFile(fname, mode='w', samplerate=fs, channels=y.shape[1], subtype='PCM_24') as f:
|
|
f.write(y)
|
|
f.comment = json.dumps({'rms': r})
|
|
|
|
|
|
#------------------------------
|
|
if __name__=='__main__':
|
|
fs = 44100
|
|
d = 1
|
|
t = np.arange(d*fs)/fs
|
|
x = np.sin(2*np.pi*1000*t)*10
|
|
|
|
write(x, fs, "test.wav")
|
|
y, fs, _ = read("test.wav")
|
|
|
|
print(rms(x))
|
|
print(rms(y))
|