Etienne Gaudrain 3393ba4827 Initial
2025-03-28 12:10:48 +01:00

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))