Skip to content

Commit

Permalink
Merge pull request ipython#4302 from davidosterberg/master
Browse files Browse the repository at this point in the history
Add an Audio display class
  • Loading branch information
takluyver committed Oct 23, 2013
2 parents f0f0d1a + 191ed03 commit f89a7a8
Show file tree
Hide file tree
Showing 2 changed files with 244 additions and 2 deletions.
152 changes: 152 additions & 0 deletions IPython/lib/display.py
Expand Up @@ -5,6 +5,158 @@
from os.path import exists, isfile, splitext, abspath, join, isdir
from os import walk, sep

from IPython.core.display import DisplayObject


class Audio(DisplayObject):
"""Create an audio object.
When this object is returned by an input cell or passed to the
display function, it will result in Audio controls being displayed
in the frontend (only works in the notebook).
Parameters
----------
data : numpy array, list, unicode, str or bytes
Can be a
* Numpy 1d array containing the desired waveform (mono)
* List of float or integer representing the waveform (mono)
* String containing the filename
* Bytestring containing raw PCM data or
* URL pointing to a file on the web.
If the array option is used the waveform will be normalized.
If a filename or url is used the format support will be browser
dependent.
url : unicode
A URL to download the data from.
filename : unicode
Path to a local file to load the data from.
embed : boolean
Should the image data be embedded using a data URI (True) or should
the original source be referenced. Set this to True if you want the
audio to playable later with no internet connection in the notebook.
Default is `True`, unless the keyword argument `url` is set, then
default value is `False`.
rate : integer
The sampling rate of the raw data.
Only required when data parameter is being used as an array
autoplay : bool
Set to True if the audio should immediately start playing.
Default is `False`.
Examples
--------
# Generate a sound
import numpy as np
framerate = 44100
t = np.linspace(0,5,framerate*5)
data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t))
Audio(data,rate=framerate)
Audio("http://www.nch.com.au/acm/8k16bitpcm.wav")
Audio(url="http://www.w3schools.com/html/horse.ogg")
Audio('/path/to/sound.wav')
Audio(filename='/path/to/sound.ogg')
Audio(b'RAW_WAV_DATA..)
Audio(data=b'RAW_WAV_DATA..)
"""

def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False):
if filename is None and url is None and data is None:
raise ValueError("No image data found. Expecting filename, url, or data.")
if embed is False and url is None:
raise ValueError("No url found. Expecting url when embed=False")

if url is not None and embed is not True:
self.embed = False
else:
self.embed = True
self.autoplay = autoplay
super(Audio, self).__init__(data=data, url=url, filename=filename)

if self.data is not None and not isinstance(self.data, bytes):
self.data = self._make_wav(data,rate)

def reload(self):
"""Reload the raw data from file or URL."""
import mimetypes
if self.embed:
super(Audio, self).reload()

if self.filename is not None:
self.mimetype = mimetypes.guess_type(self.filename)[0]
elif self.url is not None:
self.mimetype = mimetypes.guess_type(self.url)[0]
else:
self.mimetype = "audio/wav"

def _make_wav(self, data, rate):
""" Transform a numpy array to a PCM bytestring """
import struct
from io import BytesIO
import wave
try:
import numpy as np
data = np.array(data,dtype=float)
if len(data.shape) > 1:
raise ValueError("encoding of stereo PCM signals are unsupported")
scaled = np.int16(data/np.max(np.abs(data))*32767).tolist()
except ImportError:
maxabsvalue = float(max([abs(x) for x in data]))
scaled = [int(x/maxabsvalue*32767) for x in data]
fp = BytesIO()
waveobj = wave.open(fp,mode='wb')
waveobj.setnchannels(1)
waveobj.setframerate(rate)
waveobj.setsampwidth(2)
waveobj.setcomptype('NONE','NONE')
waveobj.writeframes(b''.join([struct.pack('<h',x) for x in scaled]))
val = fp.getvalue()
waveobj.close()
return val

def _data_and_metadata(self):
"""shortcut for returning metadata with url information, if defined"""
md = {}
if self.url:
md['url'] = self.url
if md:
return self.data, md
else:
return self.data

def _repr_html_(self):
src = """
<audio controls="controls" {autoplay}>
<source src="{src}" type="{type}" />
Your browser does not support the audio element.
</audio>
"""
return src.format(src=self.src_attr(),type=self.mimetype, autoplay=self.autoplay_attr())

def src_attr(self):
import base64
if self.embed and (self.data is not None):
data = base64=base64.b64encode(self.data).decode('ascii')
return """data:{type};base64,{base64}""".format(type=self.mimetype,
base64=data)
elif self.url is not None:
return self.url
else:
return ""

def autoplay_attr(self):
if(self.autoplay):
return 'autoplay="autoplay"'
else:
return ''

class IFrame(object):
"""
Expand Down
94 changes: 92 additions & 2 deletions examples/notebooks/Part 5 - Rich Display System.ipynb

Large diffs are not rendered by default.

0 comments on commit f89a7a8

Please sign in to comment.