Skip to content

Instantly share code, notes, and snippets.

@scottire
Last active June 1, 2020 15:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottire/7fe5e25f1654fb901fb7f024533d5508 to your computer and use it in GitHub Desktop.
Save scottire/7fe5e25f1654fb901fb7f024533d5508 to your computer and use it in GitHub Desktop.
Interactive Audio Plot using ipywidgets within Jupyter Notebook (not yet working with Jupyter Lab, see comments)
from ipywidgets import IntSlider, ToggleButton, Text
from IPython.display import display, Audio, Javascript
from scipy.io import wavfile
import numpy as np
from matplotlib import pyplot as plt
import ipywidgets
t = Text(value='0',
description='Elapsed Time:',
style = {'description_width': 'initial'},
)
fs, data = wavfile.read('./4k6c030p.wav')
a = Audio(data,
rate=fs,
autoplay=True,
element_id = 'audio_element',
)
fig, ax = plt.subplots(constrained_layout=True, figsize=(6, 4))
line = ax.axvline(0)
time = np.linspace(0, len(data) / fs, num=len(data))
ax.plot(time, data)
s = IntSlider(max=len(data) / fs,
description='Elapsed Time:',
style = {'description_width': 'initial'},
)
def update(change):
"""redraw line (update plot)"""
line.set_xdata(np.array([change.new, change.new]))
fig.canvas.draw()
s.observe(update, 'value')
display(a,t,s)
jscript = f'''
var time_check;
var stop_checking = false;
function updateTime() {{
var audio_element = document.getElementById("audio_element");
var manager = window.IPython.WidgetManager._managers[0];
if (audio_element == null) {{
clearTimeout(time_check);
return;
}}
// Change value only once after pausing
if ((!audio_element.paused) || (!stop_checking)) {{
if (audio_element.paused) stop_checking = true;
else stop_checking = false;
var model_prom = manager.get_model('{t.model_id}');
model_prom.then(function(model) {{
model.set('value', "" + audio_element.currentTime);
model.save_changes();
}});
var model_prom = manager.get_model('{s.model_id}');
model_prom.then(function(model) {{
model.set('value', Math.floor(audio_element.currentTime));
model.save_changes();
}});
}}//endif
time_check = setTimeout(updateTime, 1000);
}}
updateTime();
'''
Javascript(jscript)
@delip
Copy link

delip commented May 31, 2020

Followed this verbatim in my jupyterlab notebook and the Javascript fails with this error:

Javascript Error: Cannot read property 'WidgetManager' of undefined

Any ideas why?

@scottire
Copy link
Author

Hmm... I used Jupyter Notebooks and potentially there’s something different about IPython within Javascript with JupyterLab.
Try calling %matplotlib inline or %matplotlib widget before running this.

@delip
Copy link

delip commented Jun 1, 2020

Thanks! I did that. Then I had to pip install ipympl, and then chase down this issue. Now I see this, which is progress, but the slider still doesn't seem to do what you mention here. And that error is still around. Not sure if Jupyterlab does things differently from notebook. I inspected the HTML and there is an audio_element div.

image

@scottire
Copy link
Author

scottire commented Jun 1, 2020

That's good that you now have ipywidgets usable in JupyterLab. Unfortunately, it seems it's not possible to get the WidgetManager from within JupyterLab. I did a bit of reading around and it seems there's no equivalent of window.IPython within JupyterLab, which seems to have been done for security reasons so arbitrary javascript can't be run. It would be great if it was possible to get the currentTime from the audio element by some other means but I'm not sure about how/if that can be done from within JupyterLab, other than building a new jupyterlab extension.

@scottire
Copy link
Author

scottire commented Jun 1, 2020

@delip I was able to find another method to get the playhead alongside playable audio within Jupyter Lab.
See here: https://gist.github.com/scottire/654019e88e6225c15a68006ab4a3ba98
This already visualises the audio, but the on_time_update callback can be used to update any visualisations playhead using matplotlib like in the above code with line.set_xdata(np.array([time, time]).

You'll need to follow the instructions to install jp_proxy_widget with JupyterLab.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment