Skip to content

Commit a232707

Browse files
committed
Completed spectrogram.
1 parent eb84834 commit a232707

File tree

3 files changed

+55
-42
lines changed

3 files changed

+55
-42
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ RUN ["pip", "install", "numpy"]
66

77
RUN ["pip", "install", "matplotlib"]
88

9+
RUN ["pip", "install", "scipy"]
10+
911
COPY . /home
1012

1113
WORKDIR /home

constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
TEXT_GEN = "🎧 gen [name] [harmonics] [duration] ::: Generates a sound wave with the given name and number of harmonics, lasting [duration] ms. If no name provided, name will be generated. Harmonics number equals 10 by default. Duration equals 100 (ms) by default."
1919
TEXT_NOT_LOADED = "🤔 I couldn't find sound wave \"%s\", did you load it? "
2020
TEXT_INVALID_SYNTAX = "🤔 I couldn't understand that. Try this command:"
21-
TEXT_INVALID_SYNTAX_PLOT_WINDOW_T = "🤔 Oops. If you specify -w, you need to enter a starting and ending timestamp, like so: -w 200-500. Ending timestamp must be larger than starting timestamp."
21+
TEXT_INVALID_SYNTAX_PLOT_WINDOW_T = "🤔 Oops. If you specify -w, you need to enter a number, like so: -w 300."
2222
TEXT_INVALID_SYNTAX_PLOT_WINDOW = "🤔 Oops, something went wrong. One or more sound waves specified are not available at the specified timestamp."
2323
TEXT_INVALID_SYNTAX_PLOT_WINDOW_F = "🤔 Oops, %s is not a valid window function. Choose \"none\", \"hamming\" or \"hanning\"."
2424
TEXT_INVALID_SYNTAX_PLOT_TYPE = "🤔 Oops, %s is not a valid plot type. Choose \"waveform\", \"spectrogram\" or \"histogram\"."
2525
TEXT_ERROR_WRITING = "❌ There was an error while saving the wave "
2626
TEXT_GENERATED = "💽 You're officially an artist. Here's your sound wave: "
2727
TEXT_PLOTTING = "📈 Plotting..."
28+
TEXT_INVALID_SYNTAX_PLOT_TOO_MANY = "🤔 Oops, %s only support plotting 1 sound wave at a time."

main.py

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import os
99
import regex as re
1010
from constants import *
11+
from scipy import signal
1112

1213

1314
# P and R parameters for cleaning.
@@ -150,7 +151,7 @@ def plot_waves(
150151
if plot_type == "waveform":
151152

152153
plt.ylabel("Amplitude")
153-
plt.xlabel("Time")
154+
plt.xlabel("Time [s]")
154155

155156
for sw in sound_waves:
156157
title += f" {sw.name}.wav"
@@ -171,18 +172,17 @@ def plot_waves(
171172
if plot_type == "histogram":
172173

173174
plt.ylabel("Magnitude")
174-
plt.xlabel("Frequency")
175+
plt.xlabel("Frequency [Hz]")
175176
plt.xscale('log')
176177
plt.yscale('log')
177178

178179
if window_t == None:
179-
window_t = (0, 100)
180-
window_dur = (window_t[1] - window_t[0]) / 1000
180+
window_t = 100
181+
window_dur = window_t / 1000
181182

182183
for sw in sound_waves:
183184

184185
title += f" {sw.name}.wav"
185-
_, noise_borders = sw.find_endpoints(500, 5000)
186186
N = int(sw.wave.getframerate() * window_dur)
187187
f = sw.wave.getframerate() * np.arange(N / 2) / N
188188
# TODO check if okay to simply plot different freqs
@@ -199,28 +199,44 @@ def plot_waves(
199199

200200
if plot_type == "spectrogram":
201201

202-
plt.ylabel("Magnitude")
203-
plt.xlabel("Frequency")
202+
if len(sound_waves) > 1:
203+
raise ValueError("Spectrogram can only plot 1 sound wave")
204+
205+
sw = sound_waves[0]
206+
M = 1024
207+
208+
if window_func == "none" or window_func == None:
209+
freqs, times, Sx = signal.spectrogram(
210+
sw.values,
211+
fs=sw.wave.getframerate(),
212+
nperseg=M,
213+
noverlap=M - window_t,
214+
detrend=False, scaling='spectrum'
215+
)
216+
elif window_func in ["hamming", "hanning"]:
217+
freqs, times, Sx = signal.spectrogram(
218+
sw.values,
219+
fs=sw.wave.getframerate(),
220+
window=window_func,
221+
nperseg=M,
222+
noverlap=M - window_t,
223+
detrend=False, scaling='spectrum'
224+
)
225+
else:
226+
raise ValueError(
227+
"window_func argument can only be one of [None, \"hamming\", \"hanning\"]")
204228

205-
if window_t == None:
206-
window_t = (0, 100)
207-
window_dur = (window_t[1] - window_t[0]) / 1000
229+
f, ax = plt.subplots(figsize=(6, 4))
230+
ax.pcolormesh(times, freqs / 1000, 10 * np.log10(Sx), cmap='viridis')
231+
ax.set_ylabel('Frequency [kHz]')
232+
ax.set_xlabel('Time [s]')
233+
ax.set_title(f"{sw.name}.wav")
208234

209-
for sw in sound_waves:
210-
211-
title += f" {sw.name}.wav"
212-
_, noise_borders = sw.find_endpoints(500, 5000)
213-
N = int(sw.wave.getframerate() * window_dur)
214-
f = sw.wave.getframerate() * np.arange(N / 2) / N
215-
T = window_dur
216-
# TODO check if okay to simply plot different freqs
217-
y = dft(sw, window_t=window_t, window_func=window_func)
218-
plt.imshow(y, origin="lower", cmap="viridis",
219-
extent=(0, T, 0, sw.wave.getframerate() / 2 / 1000))
220-
clr = np.random.rand(3,)
221-
if not sw.cleaned:
222-
for xc in noise_borders:
223-
plt.axvline(x=xc, color=clr)
235+
_, noise_borders = sw.find_endpoints(500, 5000)
236+
clr = np.random.rand(3,)
237+
if not sw.cleaned:
238+
for xc in noise_borders:
239+
plt.axvline(x=xc, color=clr)
224240

225241
plt.legend()
226242
plt.show()
@@ -301,21 +317,17 @@ def dft(sw: SoundWave, window_t: int, window_func: str):
301317
Discrete Fourier transform. Window_t in ms.
302318
"""
303319

304-
if window_t == None:
305-
window_t = (0, len(sw.values))
306-
window_dur = (window_t[1] - window_t[0]) / 1000
320+
window_dur = window_t / 1000
307321
window_func = window_func.lower()
308322

309323
# Calculate number of samples
310324
N = int(sw.wave.getframerate() * window_dur)
311325

312326
# TODO check if we should be cutting this at all
313-
t_start = int(window_t[0] / 1000 * sw.wave.getframerate())
314-
t_end = t_start + N
315-
y = sw.values[t_start: t_end]
327+
y = sw.values
316328

317329
if len(y) == 0:
318-
raise ValueError()
330+
raise ValueError("sw.values cannot be empty")
319331
if window_func != "none":
320332
if window_func == "hamming":
321333
y = y * np.hamming(N)
@@ -452,7 +464,7 @@ def main():
452464
elif func == "plot":
453465

454466
plot_type = "waveform"
455-
window_t = None
467+
window_t = 100
456468
window_f = "none"
457469
to_compare = []
458470

@@ -474,14 +486,7 @@ def main():
474486

475487
if prev == "-w":
476488
try:
477-
tmp = arg.split("-")
478-
timestamp_start = int(tmp[0])
479-
timestamp_end = int(tmp[1])
480-
if timestamp_start - timestamp_end >= 0:
481-
print(TEXT_INVALID_SYNTAX_PLOT_WINDOW_T)
482-
err = True
483-
break
484-
window_t = (timestamp_start, timestamp_end)
489+
window_t = int(arg)
485490
except:
486491
print(TEXT_INVALID_SYNTAX_PLOT_WINDOW_T)
487492
err = True
@@ -524,6 +529,11 @@ def main():
524529
to_compare = sound_waves.values()
525530

526531
print(TEXT_PLOTTING)
532+
533+
if plot_type == "spectrogram" and len(to_compare) > 1:
534+
print(TEXT_INVALID_SYNTAX_PLOT_TOO_MANY % plot_type)
535+
continue
536+
527537
# try:
528538
plot_waves(to_compare, plot_type=plot_type,
529539
window_t=window_t, window_func=window_f)

0 commit comments

Comments
 (0)