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