@@ -54,6 +54,12 @@ class Audio(DisplayObject):
5454 autoplay : bool
5555 Set to True if the audio should immediately start playing.
5656 Default is `False`.
57+ normalize : bool
58+ Whether audio should be normalized (rescaled) to the maximum possible
59+ range. Default is `True`. When set to `False`, `data` must be between
60+ -1 and 1 (inclusive), otherwise an error is raised.
61+ Applies only when `data` is a list or array of samples; other types of
62+ audio are never normalized.
5763
5864 Examples
5965 --------
@@ -83,7 +89,7 @@ class Audio(DisplayObject):
8389 """
8490 _read_flags = 'rb'
8591
86- def __init__ (self , data = None , filename = None , url = None , embed = None , rate = None , autoplay = False ):
92+ def __init__ (self , data = None , filename = None , url = None , embed = None , rate = None , autoplay = False , normalize = True ):
8793 if filename is None and url is None and data is None :
8894 raise ValueError ("No audio data found. Expecting filename, url, or data." )
8995 if embed is False and url is None :
@@ -99,7 +105,7 @@ def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, au
99105 if self .data is not None and not isinstance (self .data , bytes ):
100106 if rate is None :
101107 raise ValueError ("rate must be specified when data is a numpy array or list of audio samples." )
102- self .data = Audio ._make_wav (data , rate )
108+ self .data = Audio ._make_wav (data , rate , normalize )
103109
104110 def reload (self ):
105111 """Reload the raw data from file or URL."""
@@ -115,16 +121,16 @@ def reload(self):
115121 self .mimetype = "audio/wav"
116122
117123 @staticmethod
118- def _make_wav (data , rate ):
124+ def _make_wav (data , rate , normalize ):
119125 """ Transform a numpy array to a PCM bytestring """
120126 import struct
121127 from io import BytesIO
122128 import wave
123129
124130 try :
125- scaled , nchan = Audio ._validate_and_normalize_with_numpy (data )
131+ scaled , nchan = Audio ._validate_and_normalize_with_numpy (data , normalize )
126132 except ImportError :
127- scaled , nchan = Audio ._validate_and_normalize_without_numpy (data )
133+ scaled , nchan = Audio ._validate_and_normalize_without_numpy (data , normalize )
128134
129135 fp = BytesIO ()
130136 waveobj = wave .open (fp ,mode = 'wb' )
@@ -139,7 +145,7 @@ def _make_wav(data, rate):
139145 return val
140146
141147 @staticmethod
142- def _validate_and_normalize_with_numpy (data ):
148+ def _validate_and_normalize_with_numpy (data , normalize ):
143149 import numpy as np
144150
145151 data = np .array (data , dtype = float )
@@ -154,21 +160,32 @@ def _validate_and_normalize_with_numpy(data):
154160 data = data .T .ravel ()
155161 else :
156162 raise ValueError ('Array audio input must be a 1D or 2D array' )
157- scaled = np .int16 (data / np .max (np .abs (data ))* 32767 ).tolist ()
163+
164+ max_abs_value = np .max (np .abs (data ))
165+ normalization_factor = Audio ._get_normalization_factor (max_abs_value , normalize )
166+ scaled = np .int16 (data / normalization_factor * 32767 ).tolist ()
158167 return scaled , nchan
159168
169+
160170 @staticmethod
161- def _validate_and_normalize_without_numpy (data ):
171+ def _validate_and_normalize_without_numpy (data , normalize ):
162172 try :
163- maxabsvalue = float (max ([abs (x ) for x in data ]))
173+ max_abs_value = float (max ([abs (x ) for x in data ]))
164174 except TypeError :
165175 raise TypeError ('Only lists of mono audio are '
166176 'supported if numpy is not installed' )
167177
168- scaled = [int (x / maxabsvalue * 32767 ) for x in data ]
178+ normalization_factor = Audio ._get_normalization_factor (max_abs_value , normalize )
179+ scaled = [int (x / normalization_factor * 32767 ) for x in data ]
169180 nchan = 1
170181 return scaled , nchan
171182
183+ @staticmethod
184+ def _get_normalization_factor (max_abs_value , normalize ):
185+ if not normalize and max_abs_value > 1 :
186+ raise ValueError ('Audio data must be between -1 and 1 when normalize=False.' )
187+ return max_abs_value if normalize else 1
188+
172189 def _data_and_metadata (self ):
173190 """shortcut for returning metadata with url information, if defined"""
174191 md = {}
0 commit comments