from IPython.display import DisplayObject
class Audio(DisplayObject):
_read_flags = 'rb'
def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, maxvalue=None):
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
self.maxvalue = maxvalue
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:
nchan = 1
elif len(data.shape) == 2:
# In wave files,channels are interleaved. E.g.,
# "L1R1L2R2..." for stereo. See
# http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx
# for channel ordering
nchan = data.shape[0]
data = data.T.ravel()
else:
raise ValueError('Array audio input must be a 1D or 2D array')
if self.maxvalue is None:
maxvalue = np.max(np.abs(data))
else:
maxvalue = self.maxvalue
scaled = np.int16(data/maxvalue * 32767).tolist()
except ImportError:
# check that it is a "1D" list
idata = iter(data) # fails if not an iterable
try:
iter(idata.next())
raise TypeError('Only lists of mono audio are '
'supported if numpy is not installed')
except TypeError:
# this means it's not a nested list, which is what we want
pass
if self.maxvalue is None:
maxvalue = float(max([abs(x) for x in data]))
else:
maxvalue = self.maxvalue
scaled = [int(x/maxvalue * 32767) for x in data]
nchan = 1
fp = BytesIO()
waveobj = wave.open(fp,mode='wb')
waveobj.setnchannels(nchan)
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 ''
print("This cell defines the Audio class.")