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