I have a 16-sec audio signal. I want to divide my signal into 16 1-sec signals with a for loop and calculate its spectrogram. Let's assume the raw signal is like the figure that I have attached and I want to calculate and plot the spectrogram of each segment with a for loop exactly like the picture. enter image description here
I appreciate your help in advance.
The code that I used to plot the 1-sec signals is:
ncols = 4
nrows = len(segment) // ncols (len(segment) % ncols > 0) # finding the best number of rows to plot the segments
fig = plt.figure(figsize=(22,19))
plt.subplots_adjust(hspace=0.5,wspace=0.4)
plt.suptitle('Raw segmented signal \n \n', fontweight = 'bold',size = 20)
for i in range (0,16):
plt.subplot(nrows, ncols, i 1)
plt.plot(segment[i])
plt.title('Segment: ' str(i 1), fontweight = 'bold',size = 15)
plt.ylabel('Amplitude (mV)', fontweight = 'bold')
plt.xlabel('Samples',fontweight = 'bold')
plt.ylim([-1.5, 1.5])
plt.xlim(xmin = 0, xmax = len(segment[i]))
fig.tight_layout()
plt.show()
CodePudding user response:
Here's an answer that might help. It uses np.split to divide the 16 second clip into the 1 second clips so there's a built-in assumption that the clip is 16 seconds long (i.e. it won't divide a clip of an arbitrary length into 1-second pieces) but since your question is specifically about getting 16 one second clips from a 16 second clip, maybe it is enough:
import numpy as np
import librosa
import matplotlib.pyplot as plt
NCOLS = 4
NROWS = 4
FILENAME = 'sound.wav'
sound, rate = librosa.load(FILENAME, sr=None)
segments = np.split(sound, NCOLS*NROWS)
fig = plt.figure(figsize=(22, 19))
plt.subplots_adjust(hspace=0.5, wspace=0.4)
plt.suptitle('Raw segmented signal \n \n', fontweight='bold', size=20)
for i in range (0, NROWS * NCOLS):
plt.subplot(NROWS, NCOLS, i 1)
plt.plot(segments[i])
plt.title('Segment: ' str(i 1), fontweight='bold', size=15)
plt.ylabel('Amplitude (mV)', fontweight='bold')
plt.xlabel('Samples', fontweight='bold')
plt.ylim([-1.5, 1.5])
plt.xlim(xmin=0, xmax=len(segments[i]))
fig.tight_layout()
plt.show()
So that will plot the waveforms and then for the spectrograms you can run this:
fig = plt.figure(figsize=(22,19))
plt.subplots_adjust(hspace=0.5,wspace=0.4)
plt.suptitle('Spectrograms of segments \n \n', fontweight='bold', size=20)
for i in range (0, NROWS * NCOLS):
plt.subplot(NROWS, NCOLS, i 1)
plt.specgram(segments[i], Fs=rate)
plt.title('Segment: ' str(i 1), fontweight='bold', size=15)
fig.tight_layout()
plt.show()
If you did want to split out a clip of arbitrary length into 1-second clips you could replace the np.split line above with:
segments = [sound[i:i rate] for i in range(0, len(sound), rate)]
This works because rate
is equivalent to the number of samples in one second. You'd have to do something more if you were looking for a different clip length. Note: the last segment won't be equal to a full second if the original clip was not actually divisible by whole seconds. Other note: lots of the rest of this code assumes you end up with 16 segments.
The original question did suggest that the spectrogram be computed with librosa and a follow-on comment clarified that the hope was that the spectrogram be computed solely using librosa instead of with pyplot.specgram. Here is an example for doing this (note: seemed to be noticeably slower than using pyplot.spectrogram on my computer):
def specgram_librosa(segment, rate, ax, win_length=256, hop_length=64):
D = librosa.stft(segment, hop_length=hop_length, win_length=win_length)
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
img = librosa.display.specshow(S_db,
sr=rate,
x_axis='s',
y_axis='linear',
ax=ax)
return img
fig, ax = plt.subplots(NROWS, NCOLS, figsize=(22, 19))
ax = ax.flatten()
plt.subplots_adjust(hspace=0.5, wspace=0.4)
plt.suptitle('Spectrograms of segments \n \n', fontweight='bold', size=20)
for i in range (0, NROWS * NCOLS):
ax[i].set_title('Segment: ' str(i 1), fontweight='bold', size=15)
img = specgram_librosa(segments[i], rate, ax[i])
fig.colorbar(img, ax=ax[i], format="% 2.f dB")
fig.tight_layout()
plt.show()