Skip to content

Commit 49139ee

Browse files
committed
Merge pull request #128 from nicktimko/simple-sum
Implement radd to support sum builtin
2 parents 41555be + 3ccc956 commit 49139ee

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

pydub/audio_segment.py

+19-10
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ def __init__(self, data=None, *args, **kwargs):
109109
self.sample_width = kwargs.pop("sample_width", None)
110110
self.frame_rate = kwargs.pop("frame_rate", None)
111111
self.channels = kwargs.pop("channels", None)
112-
112+
113113
audio_params = (self.sample_width, self.frame_rate, self.channels)
114-
114+
115115
# prevent partial specification of arguments
116116
if any(audio_params) and None in audio_params:
117117
raise MissingAudioParameter("Either all audio parameters or no parameter must be specified")
@@ -120,9 +120,9 @@ def __init__(self, data=None, *args, **kwargs):
120120
elif self.sample_width is not None:
121121
if len(data) % (self.sample_width * self.channels) != 0:
122122
raise ValueError("data length must be a multiple of '(sample_width * channels)'")
123-
123+
124124
self.frame_width = self.channels * self.sample_width
125-
self._data = data
125+
self._data = data
126126

127127
# keep support for 'metadata' until audio params are used everywhere
128128
elif kwargs.get('metadata', False):
@@ -163,25 +163,25 @@ def __init__(self, data=None, *args, **kwargs):
163163
self.frame_width = self.channels * self.sample_width
164164

165165
super(AudioSegment, self).__init__(*args, **kwargs)
166-
166+
167167
@property
168168
def raw_data(self):
169169
"""
170170
public access to the raw audio data as a bytestring
171171
"""
172172
return self._data
173-
173+
174174

175175
def get_array_of_samples(self):
176176
"""
177177
returns the raw_data as an array of samples
178178
"""
179179
return array.array(self.array_type, self._data)
180-
180+
181181
@property
182182
def array_type(self):
183183
return get_array_type(self.sample_width * 8)
184-
184+
185185
def __len__(self):
186186
"""
187187
returns the length of this audio segment in milliseconds
@@ -262,6 +262,15 @@ def __add__(self, arg):
262262
else:
263263
return self.apply_gain(arg)
264264

265+
def __radd__(self, rarg):
266+
"""
267+
Permit use of sum() builtin with an iterable of AudioSegments
268+
"""
269+
if rarg == 0:
270+
return self
271+
raise TypeError("Gains must be the second addend after the "
272+
"AudioSegment")
273+
265274
def __sub__(self, arg):
266275
if isinstance(arg, AudioSegment):
267276
raise TypeError("AudioSegment objects can't be subtracted from "
@@ -368,7 +377,7 @@ def from_file(cls, file, format=None, **kwargs):
368377
if format:
369378
format = format.lower()
370379
format = AUDIO_FILE_EXT_ALIASES.get(format, format)
371-
380+
372381
def is_format(f):
373382
f = f.lower()
374383
if format == f:
@@ -452,7 +461,7 @@ def from_wav(cls, file):
452461
@classmethod
453462
def from_raw(cls, file, **kwargs):
454463
return cls.from_file(file, 'raw', sample_width=kwargs['sample_width'], frame_rate=kwargs['frame_rate'], channels=kwargs['channels'])
455-
464+
456465
@classmethod
457466
def _from_safe_wav(cls, file):
458467
file = _fd_or_path_or_tempfile(file, 'rb', tempfile=False)

test/test.py

+31-17
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,20 @@ def test_append(self):
127127
self.assertEqual(len(merged1), len(self.seg1) + len(self.seg3) - 100)
128128
self.assertEqual(len(merged2), len(self.seg2) + len(self.seg3) - 100)
129129

130+
def test_sum(self):
131+
def gen():
132+
yield self.seg1
133+
yield self.seg2
134+
yield self.seg3
135+
136+
try:
137+
summed = sum(gen())
138+
except TypeError as e:
139+
if "unsupported operand" in str(e):
140+
self.fail("Could not sum() audio segments.")
141+
else:
142+
raise
143+
130144
def test_volume_with_add_sub(self):
131145
quieter = self.seg1 - 6
132146
self.assertAlmostEqual(ratio_to_db(quieter.rms, self.seg1.rms),
@@ -242,7 +256,7 @@ def test_indexing(self):
242256
for part in short:
243257
rebuilt1 += part
244258

245-
rebuilt2 = sum([part for part in short], short[:0])
259+
rebuilt2 = sum([part for part in short])
246260

247261
self.assertTrue(short._data == rebuilt1._data)
248262
self.assertTrue(short._data == rebuilt2._data)
@@ -662,18 +676,18 @@ def test_max_dBFS(self):
662676
sine_minus_3_dbfs = Sine(1000).to_audio_segment(volume=-3.0)
663677
self.assertAlmostEqual(-0.0, sine_0_dbfs.max_dBFS, 2)
664678
self.assertAlmostEqual(-3.0, sine_minus_3_dbfs.max_dBFS, 2)
665-
679+
666680
def test_array_type(self):
667681
self.assertEqual(self.seg1.array_type, "h")
668682
self.assertEqual(self.seg2.array_type, "h")
669683
self.assertEqual(self.seg3.array_type, "h")
670684
self.assertEqual(self.mp3_seg_party.array_type, "h")
671-
685+
672686
silence = AudioSegment.silent(50)
673687
self.assertEqual(silence.array_type, "h")
674688
self.assertEqual(silence.set_sample_width(1).array_type, "b")
675689
self.assertEqual(silence.set_sample_width(4).array_type, "i")
676-
690+
677691
def test_sample_array(self):
678692
samples = Sine(450).to_audio_segment().get_array_of_samples()
679693
self.assertEqual(
@@ -806,34 +820,34 @@ def test_opening_mp3_file_fails(self):
806820

807821
func = partial(AudioSegment.from_file, self.mp3_file, format="mp3")
808822
self.assertRaises(OSError, func)
809-
823+
810824
def test_init_AudioSegment_data_buffer(self):
811825
seg = AudioSegment(data = "\0" * 34, sample_width=2, frame_rate=4, channels=1)
812-
826+
813827
self.assertEqual(seg.duration_seconds, 4.25)
814-
828+
815829
self.assertEqual(seg.sample_width, 2)
816-
830+
817831
self.assertEqual(seg.frame_rate, 4)
818-
819-
832+
833+
820834
def test_init_AudioSegment_data_buffer_with_missing_args_fails(self):
821-
835+
822836
func = partial(AudioSegment, data = "\0" * 16, sample_width=2, frame_rate=2)
823837
self.assertRaises(MissingAudioParameter, func)
824-
838+
825839
func = partial(AudioSegment, data = "\0" * 16, sample_width=2, channels=1)
826840
self.assertRaises(MissingAudioParameter, func)
827-
841+
828842
func = partial(AudioSegment, data = "\0" * 16, frame_rate=2, channels=1)
829843
self.assertRaises(MissingAudioParameter, func)
830-
831-
844+
845+
832846
def test_init_AudioSegment_data_buffer_with_bad_values_fails(self):
833-
847+
834848
func = partial(AudioSegment, data = "\0" * 14, sample_width=4, frame_rate=2, channels=1)
835849
self.assertRaises(ValueError, func)
836-
850+
837851

838852
def test_exporting(self):
839853
seg = AudioSegment.from_wav(self.wave_file)

0 commit comments

Comments
 (0)