Skip to content

Commit

Permalink
implements phaser. closes #15
Browse files Browse the repository at this point in the history
  • Loading branch information
rabitt committed Aug 24, 2016
1 parent 3592d3c commit cc043f0
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 2 deletions.
63 changes: 61 additions & 2 deletions sox/transform.py
Expand Up @@ -1528,8 +1528,67 @@ def pad(self, start_duration=0.0, end_duration=0.0):

return self

def phaser(self):
raise NotImplementedError
def phaser(self, gain_in=0.8, gain_out=0.74, delay=3, decay=0.4, speed=0.5,
modulation_shape='sinusoidal'):
'''Apply a phasing effect to the audio.
Parameters
----------
gain_in : float, default=0.8
Input volume between 0 and 1
gain_out: float, default=0.74
Output volume between 0 and 1
delay : float, default=3
Delay in miliseconds between 0 and 5
decay : float, default=0.4
Decay relative to gain_in, between 0.1 and 0.5.
speed : float, default=0.5
Modulation speed in Hz, between 0.1 and 2
modulation_shape : str, defaul='sinusoidal'
Modulation shpae. One of 'sinusoidal' or 'triangular'
See Also
--------
flanger, tremolo
'''
if not is_number(gain_in) or gain_in <= 0 or gain_in > 1:
raise ValueError("gain_in must be a number between 0 and 1.")

if not is_number(gain_out) or gain_out <= 0 or gain_out > 1:
raise ValueError("gain_out must be a number between 0 and 1.")

if not is_number(delay) or delay <= 0 or delay > 5:
raise ValueError("delay must be a positive number.")

if not is_number(decay) or decay < 0.1 or decay > 0.5:
raise ValueError("decay must be a number between 0.1 and 0.5.")

if not is_number(speed) or speed < 0.1 or speed > 2:
raise ValueError("speed must be a positive number.")

if modulation_shape not in ['sinusoidal', 'triangular']:
raise ValueError(
"modulation_shape must be one of 'sinusoidal', 'triangular'."
)

effect_args = [
'phaser',
'{}'.format(gain_in),
'{}'.format(gain_out),
'{}'.format(delay),
'{}'.format(decay),
'{}'.format(speed)
]

if modulation_shape == 'sinusoidal':
effect_args.append('-s')
elif modulation_shape == 'triangular':
effect_args.append('-t')

self.effects.extend(effect_args)
self.effects_log.append('phaser')

return self

def pitch(self, n_semitones, quick=False):
'''Pitch shift the audio without changing the tempo.
Expand Down
133 changes: 133 additions & 0 deletions tests/test_transform.py
Expand Up @@ -1932,6 +1932,127 @@ def test_end_duration_invalid(self):
tfm.pad(end_duration='foo')


class TestTransformerPhaser(unittest.TestCase):

def test_default(self):
tfm = new_transformer()
tfm.phaser()

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '0.74', '3', '0.4', '0.5', '-s']
self.assertEqual(expected_args, actual_args)

actual_log = tfm.effects_log
expected_log = ['phaser']
self.assertEqual(expected_log, actual_log)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_gain_in_valid(self):
tfm = new_transformer()
tfm.phaser(gain_in=0.5)

actual_args = tfm.effects
expected_args = ['phaser', '0.5', '0.74', '3', '0.4', '0.5', '-s']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_gain_in_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(gain_in=0)

def test_gain_out_valid(self):
tfm = new_transformer()
tfm.phaser(gain_out=1.0)

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '1.0', '3', '0.4', '0.5', '-s']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_gain_out_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(gain_out=1.1)

def test_delay_valid(self):
tfm = new_transformer()
tfm.phaser(delay=5)

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '0.74', '5', '0.4', '0.5', '-s']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_delay_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(delay=None)

def test_decay_valid(self):
tfm = new_transformer()
tfm.phaser(decay=0.1)

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '0.74', '3', '0.1', '0.5', '-s']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_decay_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(decay=0.0)

def test_speed_valid(self):
tfm = new_transformer()
tfm.phaser(speed=2)

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '0.74', '3', '0.4', '2', '-s']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_speed_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(speed=-1)

def test_modulation_shape_valid(self):
tfm = new_transformer()
tfm.phaser(modulation_shape='triangular')

actual_args = tfm.effects
expected_args = ['phaser', '0.8', '0.74', '3', '0.4', '0.5', '-t']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_modulation_shape_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
tfm.phaser(modulation_shape='square')


class TestTransformerPitch(unittest.TestCase):

def test_default(self):
Expand Down Expand Up @@ -2692,6 +2813,18 @@ def test_factor_valid(self):
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_factor_valid_extreme(self):
tfm = new_transformer()
tfm.speed(2.5)

actual_args = tfm.effects
expected_args = ['speed', '2.5']
self.assertEqual(expected_args, actual_args)

actual_res = tfm.build(INPUT_FILE, OUTPUT_FILE)
expected_res = True
self.assertEqual(expected_res, actual_res)

def test_factor_invalid(self):
tfm = new_transformer()
with self.assertRaises(ValueError):
Expand Down

0 comments on commit cc043f0

Please sign in to comment.