##// END OF EJS Templates
Add an Audio display class...
David Österberg -
Show More
@@ -1,343 +1,488 b''
1 1 """Various display related classes.
2 2
3 3 Authors : MinRK, gregcaporaso, dannystaple
4 4 """
5 5 from os.path import exists, isfile, splitext, abspath, join, isdir
6 6 from os import walk, sep
7 import base64
8 import struct
9 from io import BytesIO
10 import wave
11 import mimetypes
12
13 import numpy as np
14 from IPython.core.display import DisplayObject
15
16
17 class Audio(DisplayObject):
18 """Create an audio object.
19
20 When this object is returned by an input cell or passed to the
21 display function, it will result in Audio controls being displayed
22 in the frontend (only works in the notebook).
23
24 Parameters
25 ----------
26 data : numpy array, unicode, str or bytes
27 Can be a
28 * Numpy array containing the desired waveform,
29 * String containingvthe filename
30 * Bytestring containing raw PCM data or
31 * URL pointing to a file on the web.
32
33 If the array option is used the waveform will be normalized.
34
35 If a filename or url is used the format support will be browser
36 dependent.
37 url : unicode
38 A URL to download the data from.
39 filename : unicode
40 Path to a local file to load the data from.
41 embed : boolean
42 Should the image data be embedded using a data URI (True) or be
43 loaded using an <img> tag. Set this to True if you want the image
44 to be viewable later with no internet connection in the notebook.
45
46 Default is `True`, unless the keyword argument `url` is set, then
47 default value is `False`.
48 rate : integer
49 The sampling rate of the raw data.
50 Only required when data parameter is being used as an array
51 autoplay : bool
52 Set to True if the audio should immediately start playing.
53 Default is `False`.
54
55 Examples
56 --------
57
58 # Generate a sound
59 import numpy as np
60 framerate = 44100
61 t = np.linspace(0,5,framerate*5)
62 data = np.sin(2*np.pi*440*np.sin(10*t**2))
63 Audio(data,rate=framerate)
64
65 Audio("http://www.nch.com.au/acm/8k16bitpcm.wav")
66 Audio(url="http://media.bradsucks.net/albums/ooi-128/01_-_Brad_Sucks_-_Dropping_out_of_School.mp3")
67 Audio(url="http://www.w3schools.com/html/horse.ogg", embed=True)
68
69 Audio('/path/to/sound.wav')
70 Audio(filename='/path/to/sound.ogg')
71
72 Audio(b'RAW_WAV_DATA..)
73 Audio(data=b'RAW_WAV_DATA..)
7 74
75 """
76
77 def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False):
78 if filename is None and url is None and data is None:
79 raise ValueError("No image data found. Expecting filename, url, or data.")
80 if embed is False and url is None:
81 raise ValueError("No url found. Expecting url when embed=False")
82
83 if url is not None and embed is not True:
84 self.embed = False
85 else:
86 self.embed = True
87 self.autoplay = autoplay
88 super(Audio, self).__init__(data=data, url=url, filename=filename)
89
90 if self.data is not None and not isinstance(self.data, bytes):
91 self.data = self._make_wav(data,rate)
92
93 def reload(self):
94 """Reload the raw data from file or URL."""
95 if self.embed:
96 super(Audio, self).reload()
97
98 if self.filename is not None:
99 self.mimetype = mimetypes.guess_type(self.filename)[0]
100 elif self.url is not None:
101 self.mimetype = mimetypes.guess_type(self.url)[0]
102 else:
103 self.mimetype = "audio/wav"
104
105
106 def _make_wav(self,data,rate):
107 """ Transform a numpy array to a PCM bytestring """
108 data = np.array(data)
109 scaled = np.int16(data/np.max(np.abs(data)) * 32767)
110 fp = BytesIO()
111 waveobj = wave.open(fp,mode='wb')
112 waveobj.setnchannels(1)
113 waveobj.setframerate(rate)
114 waveobj.setsampwidth(2)
115 waveobj.setcomptype('NONE','NONE')
116 waveobj.writeframes(b''.join([struct.pack('<h',x) for x in scaled]))
117 val = fp.getvalue()
118 waveobj.close()
119 return val
120
121 def _data_and_metadata(self):
122 """shortcut for returning metadata with url information, if defined"""
123 md = {}
124 if self.url:
125 md['url'] = self.url
126 if md:
127 return self.data, md
128 else:
129 return self.data
130
131 def _repr_html_(self):
132 src = """
133 <audio controls="controls" {autoplay}>
134 <source src="{src}" type="{type}" />
135 Your browser does not support the audio element.
136 </audio>
137 """
138 return src.format(src=self.src_attr(),type=self.mimetype, autoplay=self.autoplay_attr())
139
140 def src_attr(self):
141 if self.embed and (self.data is not None):
142 return """data:{type};base64,{base64}""".format(type=self.mimetype, base64=base64.encodestring(self.data))
143 elif self.url is not None:
144 return self.url
145 else:
146 return ""
147
148 def autoplay_attr(self):
149 if(self.autoplay):
150 return 'autoplay="autoplay"'
151 else:
152 return ''
8 153
9 154 class IFrame(object):
10 155 """
11 156 Generic class to embed an iframe in an IPython notebook
12 157 """
13 158
14 159 iframe = """
15 160 <iframe
16 161 width="{width}"
17 162 height={height}"
18 163 src="{src}{params}"
19 164 frameborder="0"
20 165 allowfullscreen
21 166 ></iframe>
22 167 """
23 168
24 169 def __init__(self, src, width, height, **kwargs):
25 170 self.src = src
26 171 self.width = width
27 172 self.height = height
28 173 self.params = kwargs
29 174
30 175 def _repr_html_(self):
31 176 """return the embed iframe"""
32 177 if self.params:
33 178 from urllib import urlencode
34 179 params = "?" + urlencode(self.params)
35 180 else:
36 181 params = ""
37 182 return self.iframe.format(src=self.src,
38 183 width=self.width,
39 184 height=self.height,
40 185 params=params)
41 186
42 187 class YouTubeVideo(IFrame):
43 188 """Class for embedding a YouTube Video in an IPython session, based on its video id.
44 189
45 190 e.g. to embed the video on this page:
46 191
47 192 http://www.youtube.com/watch?v=foo
48 193
49 194 you would do:
50 195
51 196 vid = YouTubeVideo("foo")
52 197 display(vid)
53 198
54 199 To start from 30 seconds:
55 200
56 201 vid = YouTubeVideo("abc", start=30)
57 202 display(vid)
58 203
59 204 To calculate seconds from time as hours, minutes, seconds use:
60 205 start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds())
61 206
62 207 Other parameters can be provided as documented at
63 208 https://developers.google.com/youtube/player_parameters#parameter-subheader
64 209 """
65 210
66 211 def __init__(self, id, width=400, height=300, **kwargs):
67 212 src = "http://www.youtube.com/embed/{0}".format(id)
68 213 super(YouTubeVideo, self).__init__(src, width, height, **kwargs)
69 214
70 215 class VimeoVideo(IFrame):
71 216 """
72 217 Class for embedding a Vimeo video in an IPython session, based on its video id.
73 218 """
74 219
75 220 def __init__(self, id, width=400, height=300, **kwargs):
76 221 src="http://player.vimeo.com/video/{0}".format(id)
77 222 super(VimeoVideo, self).__init__(src, width, height, **kwargs)
78 223
79 224 class ScribdDocument(IFrame):
80 225 """
81 226 Class for embedding a Scribd document in an IPython session
82 227
83 228 Use the start_page params to specify a starting point in the document
84 229 Use the view_mode params to specify display type one off scroll | slideshow | book
85 230
86 231 e.g to Display Wes' foundational paper about PANDAS in book mode from page 3
87 232
88 233 ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book")
89 234 """
90 235
91 236 def __init__(self, id, width=400, height=300, **kwargs):
92 237 src="http://www.scribd.com/embeds/{0}/content".format(id)
93 238 super(ScribdDocument, self).__init__(src, width, height, **kwargs)
94 239
95 240 class FileLink(object):
96 241 """Class for embedding a local file link in an IPython session, based on path
97 242
98 243 e.g. to embed a link that was generated in the IPython notebook as my/data.txt
99 244
100 245 you would do::
101 246
102 247 local_file = FileLink("my/data.txt")
103 248 display(local_file)
104 249
105 250 or in the HTML notebook, just::
106 251
107 252 FileLink("my/data.txt")
108 253 """
109 254
110 255 html_link_str = "<a href='%s' target='_blank'>%s</a>"
111 256
112 257 def __init__(self,
113 258 path,
114 259 url_prefix='files/',
115 260 result_html_prefix='',
116 261 result_html_suffix='<br>'):
117 262 """
118 263 Parameters
119 264 ----------
120 265 path : str
121 266 path to the file or directory that should be formatted
122 267 directory_prefix : str
123 268 prefix to be prepended to all files to form a working link [default:
124 269 'files']
125 270 result_html_prefix : str
126 271 text to append to beginning to link [default: none]
127 272 result_html_suffix : str
128 273 text to append at the end of link [default: '<br>']
129 274 """
130 275 if isdir(path):
131 276 raise ValueError,\
132 277 ("Cannot display a directory using FileLink. "
133 278 "Use FileLinks to display '%s'." % path)
134 279 self.path = path
135 280 self.url_prefix = url_prefix
136 281 self.result_html_prefix = result_html_prefix
137 282 self.result_html_suffix = result_html_suffix
138 283
139 284 def _format_path(self):
140 285 fp = ''.join([self.url_prefix,self.path])
141 286 return ''.join([self.result_html_prefix,
142 287 self.html_link_str % (fp, self.path),
143 288 self.result_html_suffix])
144 289
145 290 def _repr_html_(self):
146 291 """return html link to file
147 292 """
148 293 if not exists(self.path):
149 294 return ("Path (<tt>%s</tt>) doesn't exist. "
150 295 "It may still be in the process of "
151 296 "being generated, or you may have the "
152 297 "incorrect path." % self.path)
153 298
154 299 return self._format_path()
155 300
156 301 def __repr__(self):
157 302 """return absolute path to file
158 303 """
159 304 return abspath(self.path)
160 305
161 306 class FileLinks(FileLink):
162 307 """Class for embedding local file links in an IPython session, based on path
163 308
164 309 e.g. to embed links to files that were generated in the IPython notebook under my/data
165 310
166 311 you would do:
167 312
168 313 local_files = FileLinks("my/data")
169 314 display(local_files)
170 315
171 316 or in the HTML notebook, just
172 317
173 318 FileLinks("my/data")
174 319
175 320 """
176 321 def __init__(self,
177 322 path,
178 323 url_prefix='files/',
179 324 included_suffixes=None,
180 325 result_html_prefix='',
181 326 result_html_suffix='<br>',
182 327 notebook_display_formatter=None,
183 328 terminal_display_formatter=None):
184 329 """
185 330 included_suffixes : list of filename suffixes to include when
186 331 formatting output [default: include all files]
187 332
188 333 See the FileLink (baseclass of LocalDirectory) docstring for
189 334 information on additional parameters.
190 335
191 336 notebook_display_formatter : func used to format links for display
192 337 in the notebook. See discussion of formatter function below.
193 338
194 339 terminal_display_formatter : func used to format links for display
195 340 in the terminal. See discussion of formatter function below.
196 341
197 342
198 343 Passing custom formatter functions
199 344 ----------------------------------
200 345 Formatter functions must be of the form:
201 346 f(dirname, fnames, included_suffixes)
202 347 dirname : the name of a directory (a string),
203 348 fnames : a list of the files in that directory
204 349 included_suffixes : a list of the file suffixes that should be
205 350 included in the output (passing None means
206 351 to include all suffixes in the output in
207 352 the built-in formatters)
208 353
209 354 returns a list of lines that should will be print in the
210 355 notebook (if passing notebook_display_formatter) or the terminal
211 356 (if passing terminal_display_formatter). This function is iterated
212 357 over for each directory in self.path. Default formatters are in
213 358 place, can be passed here to support alternative formatting.
214 359
215 360 """
216 361 if isfile(path):
217 362 raise ValueError,\
218 363 ("Cannot display a file using FileLinks. "
219 364 "Use FileLink to display '%s'." % path)
220 365 self.included_suffixes = included_suffixes
221 366 # remove trailing slashs for more consistent output formatting
222 367 path = path.rstrip('/')
223 368
224 369 self.path = path
225 370 self.url_prefix = url_prefix
226 371 self.result_html_prefix = result_html_prefix
227 372 self.result_html_suffix = result_html_suffix
228 373
229 374 self.notebook_display_formatter = \
230 375 notebook_display_formatter or self._get_notebook_display_formatter()
231 376 self.terminal_display_formatter = \
232 377 terminal_display_formatter or self._get_terminal_display_formatter()
233 378
234 379 def _get_display_formatter(self,
235 380 dirname_output_format,
236 381 fname_output_format,
237 382 fp_format,
238 383 fp_cleaner=None):
239 384 """ generate built-in formatter function
240 385
241 386 this is used to define both the notebook and terminal built-in
242 387 formatters as they only differ by some wrapper text for each entry
243 388
244 389 dirname_output_format: string to use for formatting directory
245 390 names, dirname will be substituted for a single "%s" which
246 391 must appear in this string
247 392 fname_output_format: string to use for formatting file names,
248 393 if a single "%s" appears in the string, fname will be substituted
249 394 if two "%s" appear in the string, the path to fname will be
250 395 substituted for the first and fname will be substituted for the
251 396 second
252 397 fp_format: string to use for formatting filepaths, must contain
253 398 exactly two "%s" and the dirname will be subsituted for the first
254 399 and fname will be substituted for the second
255 400 """
256 401 def f(dirname, fnames, included_suffixes=None):
257 402 result = []
258 403 # begin by figuring out which filenames, if any,
259 404 # are going to be displayed
260 405 display_fnames = []
261 406 for fname in fnames:
262 407 if (isfile(join(dirname,fname)) and
263 408 (included_suffixes == None or
264 409 splitext(fname)[1] in included_suffixes)):
265 410 display_fnames.append(fname)
266 411
267 412 if len(display_fnames) == 0:
268 413 # if there are no filenames to display, don't print anything
269 414 # (not even the directory name)
270 415 pass
271 416 else:
272 417 # otherwise print the formatted directory name followed by
273 418 # the formatted filenames
274 419 dirname_output_line = dirname_output_format % dirname
275 420 result.append(dirname_output_line)
276 421 for fname in display_fnames:
277 422 fp = fp_format % (dirname,fname)
278 423 if fp_cleaner is not None:
279 424 fp = fp_cleaner(fp)
280 425 try:
281 426 # output can include both a filepath and a filename...
282 427 fname_output_line = fname_output_format % (fp, fname)
283 428 except TypeError:
284 429 # ... or just a single filepath
285 430 fname_output_line = fname_output_format % fname
286 431 result.append(fname_output_line)
287 432 return result
288 433 return f
289 434
290 435 def _get_notebook_display_formatter(self,
291 436 spacer="&nbsp;&nbsp;"):
292 437 """ generate function to use for notebook formatting
293 438 """
294 439 dirname_output_format = \
295 440 self.result_html_prefix + "%s/" + self.result_html_suffix
296 441 fname_output_format = \
297 442 self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix
298 443 fp_format = self.url_prefix + '%s/%s'
299 444 if sep == "\\":
300 445 # Working on a platform where the path separator is "\", so
301 446 # must convert these to "/" for generating a URI
302 447 def fp_cleaner(fp):
303 448 # Replace all occurences of backslash ("\") with a forward
304 449 # slash ("/") - this is necessary on windows when a path is
305 450 # provided as input, but we must link to a URI
306 451 return fp.replace('\\','/')
307 452 else:
308 453 fp_cleaner = None
309 454
310 455 return self._get_display_formatter(dirname_output_format,
311 456 fname_output_format,
312 457 fp_format,
313 458 fp_cleaner)
314 459
315 460 def _get_terminal_display_formatter(self,
316 461 spacer=" "):
317 462 """ generate function to use for terminal formatting
318 463 """
319 464 dirname_output_format = "%s/"
320 465 fname_output_format = spacer + "%s"
321 466 fp_format = '%s/%s'
322 467
323 468 return self._get_display_formatter(dirname_output_format,
324 469 fname_output_format,
325 470 fp_format)
326 471
327 472 def _format_path(self):
328 473 result_lines = []
329 474 walked_dir = list(walk(self.path))
330 475 walked_dir.sort()
331 476 for dirname, subdirs, fnames in walked_dir:
332 477 result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes)
333 478 return '\n'.join(result_lines)
334 479
335 480 def __repr__(self):
336 481 """return newline-separated absolute paths
337 482 """
338 483 result_lines = []
339 484 walked_dir = list(walk(self.path))
340 485 walked_dir.sort()
341 486 for dirname, subdirs, fnames in walked_dir:
342 487 result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes)
343 488 return '\n'.join(result_lines)
General Comments 0
You need to be logged in to leave comments. Login now