2017-10-13 11:10:57 +00:00
|
|
|
#!/usr/bin/env python
|
2017-10-25 08:06:41 +00:00
|
|
|
""" This work is licensed under a Creative Commons Attribution 3.0 Unported
|
|
|
|
|
License.
|
2017-10-13 11:10:57 +00:00
|
|
|
Frank Zalkow, 2012-2013
|
|
|
|
|
http://www.frank-zalkow.de/en/code-snippets/create-audio-spectrograms-with-python.html?i=1
|
|
|
|
|
"""
|
|
|
|
|
# %matplotlib inline
|
|
|
|
|
import numpy as np
|
2017-10-25 08:07:17 +00:00
|
|
|
import pyaudio
|
2017-10-13 11:10:57 +00:00
|
|
|
from matplotlib import pyplot as plt
|
|
|
|
|
from pysndfile import sndio as snd
|
|
|
|
|
from numpy.lib import stride_tricks
|
|
|
|
|
""" short time fourier transform of audio signal """
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-11-14 12:24:44 +00:00
|
|
|
STFT_WINDOWS_MSEC = 20
|
|
|
|
|
STFT_WINDOW_OVERLAP = 1.0 / 3
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-11-22 09:15:08 +00:00
|
|
|
|
2017-10-13 11:10:57 +00:00
|
|
|
def stft(sig, frameSize, overlapFac=0.5, window=np.hanning):
|
|
|
|
|
win = window(frameSize)
|
|
|
|
|
hopSize = int(frameSize - np.floor(overlapFac * frameSize))
|
|
|
|
|
|
|
|
|
|
# zeros at beginning (thus center of 1st window should be for sample nr. 0)
|
|
|
|
|
# sig = (sig*255).astype(np.uint8)
|
2017-10-25 08:06:41 +00:00
|
|
|
count = int(np.floor(frameSize / 2.0))
|
2017-10-13 11:10:57 +00:00
|
|
|
samples = np.append(np.zeros(count), sig)
|
|
|
|
|
# cols for windowing
|
2017-10-25 08:06:41 +00:00
|
|
|
cols = int(np.ceil((len(samples) - frameSize) / float(hopSize)) + 1)
|
2017-10-13 11:10:57 +00:00
|
|
|
# zeros at end (thus samples can be fully covered by frames)
|
|
|
|
|
samples = np.append(samples, np.zeros(frameSize))
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
frames = stride_tricks.as_strided(
|
|
|
|
|
samples,
|
|
|
|
|
shape=(cols, frameSize),
|
|
|
|
|
strides=(samples.strides[0] * hopSize, samples.strides[0])).copy()
|
2017-10-13 11:10:57 +00:00
|
|
|
frames *= win
|
|
|
|
|
return np.fft.rfft(frames)
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-10-13 11:10:57 +00:00
|
|
|
""" scale frequency axis logarithmically """
|
2017-10-25 08:06:41 +00:00
|
|
|
|
|
|
|
|
|
2017-10-13 11:10:57 +00:00
|
|
|
def logscale_spec(spec, sr=44100, factor=20.):
|
|
|
|
|
timebins, freqbins = np.shape(spec)
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
scale = np.linspace(0, 1, freqbins)**factor
|
|
|
|
|
scale *= (freqbins - 1) / max(scale)
|
2017-10-13 11:10:57 +00:00
|
|
|
scale = np.unique(np.round(scale)).astype(np.uint32)
|
|
|
|
|
# create spectrogram with new freq bins
|
|
|
|
|
newspec = np.complex128(np.zeros([timebins, len(scale)]))
|
|
|
|
|
for i in range(0, len(scale)):
|
2017-10-25 08:06:41 +00:00
|
|
|
if i == len(scale) - 1:
|
|
|
|
|
newspec[:, i] = np.sum(spec[:, scale[i]:], axis=1)
|
2017-10-13 11:10:57 +00:00
|
|
|
else:
|
2017-10-25 08:06:41 +00:00
|
|
|
newspec[:, i] = np.sum(spec[:, scale[i]:scale[i + 1]], axis=1)
|
2017-10-13 11:10:57 +00:00
|
|
|
# list center freq of bins
|
2017-10-25 08:06:41 +00:00
|
|
|
allfreqs = np.abs(np.fft.fftfreq(freqbins * 2, 1. / sr)[:freqbins + 1])
|
2017-10-13 11:10:57 +00:00
|
|
|
freqs = []
|
|
|
|
|
for i in range(0, len(scale)):
|
2017-10-25 08:06:41 +00:00
|
|
|
if i == len(scale) - 1:
|
2017-10-13 11:10:57 +00:00
|
|
|
freqs += [np.mean(allfreqs[scale[i]:])]
|
|
|
|
|
else:
|
2017-10-25 08:06:41 +00:00
|
|
|
freqs += [np.mean(allfreqs[scale[i]:scale[i + 1]])]
|
2017-10-13 11:10:57 +00:00
|
|
|
return newspec, freqs
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-10-13 11:10:57 +00:00
|
|
|
""" generate spectrogram for aiff audio with 150ms windows and 50ms overlap"""
|
2017-10-25 08:06:41 +00:00
|
|
|
|
|
|
|
|
|
2017-10-27 13:23:22 +00:00
|
|
|
def generate_spec_frec(samples, samplerate):
|
2017-10-13 11:10:57 +00:00
|
|
|
# samplerate, samples = wav.read(audiopath)
|
|
|
|
|
# s = stft(samples, binsize)
|
2017-11-22 09:15:08 +00:00
|
|
|
s = stft(samples, samplerate * STFT_WINDOWS_MSEC // 1000,
|
|
|
|
|
STFT_WINDOW_OVERLAP)
|
2017-10-13 11:10:57 +00:00
|
|
|
sshow, freq = logscale_spec(s, factor=1.0, sr=samplerate)
|
2017-11-22 09:15:08 +00:00
|
|
|
# add epison so that log10 doesn't break
|
|
|
|
|
sshow_abs = np.abs(sshow + np.finfo(sshow.dtype).eps)
|
|
|
|
|
ims = 20. * np.log10(sshow_abs / 10e-6)
|
|
|
|
|
ims[ims < 0] = 0 #np.finfo(sshow.dtype).eps
|
2017-10-25 08:07:17 +00:00
|
|
|
return ims, freq
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-12-28 13:23:54 +00:00
|
|
|
def generate_sample_spectrogram(samples):
|
|
|
|
|
ims, _ = generate_spec_frec(samples, 22050)
|
|
|
|
|
return ims
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-10-25 08:07:17 +00:00
|
|
|
def generate_aiff_spectrogram(audiopath):
|
2017-10-25 08:06:41 +00:00
|
|
|
samples, samplerate, _ = snd.read(audiopath)
|
2017-10-27 13:23:22 +00:00
|
|
|
ims, _ = generate_spec_frec(samples, samplerate)
|
2017-10-25 08:07:17 +00:00
|
|
|
return ims
|
2017-10-13 11:10:57 +00:00
|
|
|
|
|
|
|
|
|
2017-11-22 09:15:08 +00:00
|
|
|
def plot_stft(samples,
|
|
|
|
|
samplerate,
|
|
|
|
|
binsize=2**10,
|
|
|
|
|
plotpath=None,
|
|
|
|
|
colormap="jet"):
|
2017-10-27 13:23:22 +00:00
|
|
|
(ims, freq) = generate_spec_frec(samples, samplerate)
|
2017-10-13 11:10:57 +00:00
|
|
|
timebins, freqbins = np.shape(ims)
|
|
|
|
|
plt.figure(figsize=(15, 7.5))
|
2017-10-25 08:06:41 +00:00
|
|
|
plt.imshow(
|
|
|
|
|
np.transpose(ims),
|
|
|
|
|
origin="lower",
|
|
|
|
|
aspect="auto",
|
|
|
|
|
cmap=colormap,
|
|
|
|
|
interpolation="none")
|
2017-10-13 11:10:57 +00:00
|
|
|
plt.colorbar()
|
|
|
|
|
|
|
|
|
|
plt.xlabel("time (s)")
|
|
|
|
|
plt.ylabel("frequency (hz)")
|
2017-10-25 08:06:41 +00:00
|
|
|
plt.xlim([0, timebins - 1])
|
2017-10-13 11:10:57 +00:00
|
|
|
plt.ylim([0, freqbins])
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
xlocs = np.float32(np.linspace(0, timebins - 1, 5))
|
|
|
|
|
plt.xticks(xlocs, [
|
|
|
|
|
"%.02f" % l
|
|
|
|
|
for l in (
|
|
|
|
|
(xlocs * len(samples) / timebins) + (0.5 * binsize)) / samplerate
|
|
|
|
|
])
|
|
|
|
|
ylocs = np.int16(np.round(np.linspace(0, freqbins - 1, 10)))
|
2017-10-13 11:10:57 +00:00
|
|
|
plt.yticks(ylocs, ["%.02f" % freq[i] for i in ylocs])
|
|
|
|
|
if plotpath:
|
|
|
|
|
plt.savefig(plotpath, bbox_inches="tight")
|
|
|
|
|
else:
|
|
|
|
|
plt.show()
|
|
|
|
|
plt.clf()
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-10-25 08:07:17 +00:00
|
|
|
def plot_aiff_stft(audiopath, binsize=2**10, plotpath=None, colormap="jet"):
|
|
|
|
|
samples, samplerate, _ = snd.read(audiopath)
|
|
|
|
|
plot_stft(samples, samplerate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def play_sunflower():
|
2017-11-22 09:15:08 +00:00
|
|
|
sample_r = snd.get_info(
|
|
|
|
|
'./outputs/audio/sunflowers-Alex-150-normal-589.aiff')[0]
|
|
|
|
|
snd_data_f64 = snd.read(
|
|
|
|
|
'./outputs/audio/sunflowers-Alex-150-normal-589.aiff')[0]
|
2017-10-25 08:07:17 +00:00
|
|
|
snd_data_f32 = snd_data_f64.astype(np.float32)
|
2017-10-25 10:08:03 +00:00
|
|
|
print(snd_data_f32.shape)
|
2017-10-25 08:07:17 +00:00
|
|
|
snd_data = snd_data_f32.tobytes()
|
|
|
|
|
p_oup = pyaudio.PyAudio()
|
|
|
|
|
stream = p_oup.open(
|
|
|
|
|
format=pyaudio.paFloat32, channels=1, rate=sample_r, output=True)
|
|
|
|
|
stream.write(snd_data)
|
|
|
|
|
stream.close()
|
|
|
|
|
p_oup.terminate()
|
|
|
|
|
plot_stft(snd_data_f32, sample_r)
|
|
|
|
|
|
2017-10-25 08:06:41 +00:00
|
|
|
|
2017-10-13 11:10:57 +00:00
|
|
|
if __name__ == '__main__':
|
2017-11-14 12:24:44 +00:00
|
|
|
# play_sunflower()
|
2017-11-22 09:15:08 +00:00
|
|
|
plot_aiff_stft(
|
|
|
|
|
'./outputs/story_words/Agnes/150/chicken-Agnes-150-low-1077.aiff')
|
|
|
|
|
plot_aiff_stft(
|
|
|
|
|
'./outputs/story_words/Agnes/150/chicken-Agnes-150-medium-1762.aiff')
|
2017-11-14 12:24:44 +00:00
|
|
|
# spec = generate_aiff_spectrogram('./outputs/story_words/Agnes/150/chicken-Agnes-150-low-1077.aiff')
|
|
|
|
|
# print(spec.shape)
|
2017-10-25 08:07:17 +00:00
|
|
|
# plot_aiff_stft('./outputs/sunflowers-Alex-180-normal-4763.aiff')
|
|
|
|
|
# plot_aiff_stft('./outputs/sunflowers-Victoria-180-normal-870.aiff')
|
|
|
|
|
# plot_aiff_stft('./outputs/sunflowers-Fred-180-phoneme-9733.aiff')
|
|
|
|
|
# plot_aiff_stft('./outputs/sunflowers-Fred-180-normal-6515.aiff')
|