user391339
user391339

Reputation: 8775

How can I add rectangles and labels above an audio timeseries plot?

I am working with audio timeseries and want to better label areas that are being plotted. I believe this question is general to labelling timeseries data in matplotlib, but it may be important that I am also using librosa.display.waveplot to initially plot the waveform. Currently, I am able to make the following plot using librosa and matplotlib (in a jupyter notebook):

enter image description here

I would like to make something like the following: enter image description here

The code I used is as follows, where x[1] is a simple vector containing the audio data (happens to be stereo), and target_len is the length of a recording that is repeated 3 times with some filtering. The audio file was imported using x, Fs = librosa.load("audiofile.wav", mono=False)

plt.figure(figsize=(12, 3))
librosa.display.waveplot(x[1], sr=Fs, color='black')
plt.xlabel('Time (seconds)')
plt.ylabel('Amplitude')
plt.axvspan(0, target_len, color = 'magenta', alpha=0.5, zorder=-100)
plt.axvspan(target_len, target_len*2, color = 'yellow', alpha=0.5, zorder=-100)
plt.axvspan(target_len*2, target_len*3,color = 'blue', alpha=0.5, zorder=-100)
plt.tight_layout()

My attempts at drawing rectangles directly above the audio waveform have failed, and the best I could do is create another figure above the audio figure produced by librosa, drawing a rectangle using a patch. Unfortunately the rectangle is being placed way too far away from where I want it.

enter image description here

The code for this rectangle is here (with ax.axis('off') commented out so you can see the poor placement of the figure):

from matplotlib.patches import Rectangle
fig = plt.figure(figsize=(12,3))
ax = fig.add_subplot(111) 
ax.add_patch(Rectangle((0.04, 0), 0.35, 0.05, facecolor="black"))
#ax.axis('off')
plt.tight_layout

Again, this question is fundamentally about matplotlib and figure annotation, and specifically about how to annotate the plots that librosa (or any other consumer of matplotlib for that matter) makes. It seems like librosa applies some of it's own formatting to the figure so it does not align well with what I try to draw in a new figure above it.

Upvotes: 2

Views: 676

Answers (2)

r-beginners
r-beginners

Reputation: 35115

I am not very experienced in customizing matplotlib. I created the code by referring to this page. I think there is a better way to adjust the position as it is set manually.

import librosa
import librosa.display
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import matplotlib as mpl

file_name = '/content/test.wav'
wav, sr = librosa.load(file_name, sr=44100)

target_len = 1.5

fig = plt.figure(figsize=(12,3))
ax = fig.add_subplot(111) 
librosa.display.waveplot(wav, sr, color='black')
ax.set_xlabel('Time (seconds)')
ax.set_ylabel('Amplitude')
ax.axvspan(0, target_len, color = 'magenta', alpha=0.5, zorder=-100)
ax.axvspan(target_len, target_len*2, color = 'yellow', alpha=0.5, zorder=-100)
ax.axvspan(target_len*2, target_len*3,color = 'blue', alpha=0.5, zorder=-100)
# t = fig.transFigure
# print(t)
a = ax.transAxes
print(a)
plt.text(0.25, 1.0, 'Audio 1', ha='center', va='top', transform=fig.transFigure)
plt.text(0.45, 1.0, 'Audio 2', ha='center', va='top', transform=fig.transFigure)
plt.text(0.70, 1.0, 'Audio 3', ha='center', va='top', transform=fig.transFigure)
rect1 = mpl.patches.Rectangle((0.0, 1.02), width=0.295, height=0.05, color="black", transform=ax.transAxes, clip_on=False)
rect2 = mpl.patches.Rectangle((0.30, 1.02), width=0.295, height=0.05, color="black", transform=ax.transAxes, clip_on=False)
rect3 = mpl.patches.Rectangle((0.60, 1.02), width=0.295, height=0.05, color="black", transform=ax.transAxes, clip_on=False)
ax.add_patch(rect1)
ax.add_patch(rect2)
ax.add_patch(rect3)
# plt.tight_layout()

plt.show()

BboxTransformTo(
    TransformedBbox(
        Bbox(x0=0.125, y0=0.125, x1=0.9, y1=0.88),
        BboxTransformTo(
            TransformedBbox(
                Bbox(x0=0.0, y0=0.0, x1=12.0, y1=3.0),
                Affine2D(
                    [[72.  0.  0.]
                     [ 0. 72.  0.]
                     [ 0.  0.  1.]])))))

enter image description here

Upvotes: 3

Jon Nordby
Jon Nordby

Reputation: 6259

axvspan takes two optional arguments ymin and ymax, in units relative to the height of the Y axis. 1.0 is top of Y axis, 0.0 is bottom of Y axis. So try using ymin=1.1 and ymax=1.2 or something similar.

Upvotes: 1

Related Questions