Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
[tool.poetry]
name = "pytsmod"
version = "0.2.0"
description = ""
description = "An open-source Python library for audio time-scale modification."
authors = ["Sangeon Yong <koragon2@kaist.ac.kr>"]

license = "GPL-3.0"
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.6"
numpy = "^1.16.0"
Expand All @@ -15,8 +18,8 @@ librosa = "^0.8"
pytest = "^5.2"
flake8 = "^3.8.3"

# [tool.poetry.scripts]
# tsmod = 'pytsmod.console:run'
[tool.poetry.scripts]
tsmod = 'pytsmod.console:run'

[build-system]
requires = ["poetry>=0.12"]
Expand Down
100 changes: 100 additions & 0 deletions pytsmod/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import sys
sys.path.append('./')

from pytsmod import ola, wsola
from pytsmod import phase_vocoder as pv
from pytsmod import phase_vocoder_int as pv_int
import argparse
import soundfile as sf


def run():
parser = argparse.ArgumentParser(description='Processing time-scale modification for given audio file.')
parser.add_argument('algorithm', nargs='?', choices=['ola', 'wsola', 'pv', 'pv_int', 'hp'])
# parser.add_argument('--help', action='store_true')

args, sub_args = parser.parse_known_args()

if args.algorithm == 'ola':
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str)
parser.add_argument('output_file', type=str)
parser.add_argument('alpha', type=float)
parser.add_argument('--win_type', '-wt', default='hann', type=str)
parser.add_argument('--win_size', '-ws', default=1024, type=int)
parser.add_argument('--syn_hop_size', '-sh', default=512, type=int)

params = parser.parse_args(sub_args)

x, sr = sf.read(params.input_file)

y = ola(x, params.alpha, win_type=params.win_type,
win_size=params.win_size, syn_hop_size=params.syn_hop_size)
elif args.algorithm == 'wsola':
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str)
parser.add_argument('output_file', type=str)
parser.add_argument('alpha', type=float)
parser.add_argument('--win_type', '-wt', default='hann', type=str)
parser.add_argument('--win_size', '-ws', default=1024, type=int)
parser.add_argument('--syn_hop_size', '-sh', default=512, type=int)
parser.add_argument('--tolerance', '-t', default=512, type=int)

params = parser.parse_args(sub_args)

x, sr = sf.read(params.input_file)

y = wsola(x, params.alpha, win_type=params.win_type,
win_size=params.win_size, syn_hop_size=params.syn_hop_size,
tolerance=params.tolerance)
elif args.algorithm == 'pv':
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str)
parser.add_argument('output_file', type=str)
parser.add_argument('alpha', type=float)
parser.add_argument('--win_type', '-wt', default='sin', type=str)
parser.add_argument('--win_size', '-ws', default=2048, type=int)
parser.add_argument('--syn_hop_size', '-sh', default=512, type=int)
parser.add_argument('--zero_pad', '-z', default=0, type=int)
parser.add_argument('--restore_energy', '-e', action='store_true')
parser.add_argument('--fft_shift', '-fs', action='store_true')
parser.add_argument('--phase_lock', '-pl', action='store_true')

params = parser.parse_args(sub_args)

x, sr = sf.read(params.input_file)

y = pv(x, params.alpha, win_type=params.win_type,
win_size=params.win_size, syn_hop_size=params.syn_hop_size,
zero_pad=params.zero_pad, restore_energy=params.restore_energy,
fft_shift=params.fft_shift, phase_lock=params.phase_lock)
elif args.algorithm == 'pv_int':
parser = argparse.ArgumentParser()
parser.add_argument('input_file', type=str)
parser.add_argument('output_file', type=str)
parser.add_argument('alpha', type=int)
parser.add_argument('--win_type', '-wt', default='hann', type=str)
parser.add_argument('--win_size', '-ws', default=2048, type=int)
parser.add_argument('--syn_hop_size', '-sh', default=512, type=int)
parser.add_argument('--zero_pad', '-z', default=None, type=int)
parser.add_argument('--restore_energy', '-e', action='store_true')
parser.add_argument('--fft_shift', '-fs', action='store_true')

params = parser.parse_args(sub_args)
print(params.zero_pad)

x, sr = sf.read(params.input_file)

y = pv_int(x, params.alpha, win_type=params.win_type,
win_size=params.win_size, syn_hop_size=params.syn_hop_size,
zero_pad=params.zero_pad,
restore_energy=params.restore_energy,
fft_shift=params.fft_shift)
# elif args.algorithm == 'hp':
# pass

sf.write(params.output_file, y, sr)


if __name__ == '__main__':
run()
2 changes: 1 addition & 1 deletion pytsmod/pvtsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def phase_vocoder_int(x, s, win_type='hann', win_size=2048, syn_hop_size=512,

y[c, :] = y_chan

return y
return y.squeeze()


def _find_peaks(spec):
Expand Down
155 changes: 155 additions & 0 deletions tests/test_console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import pytest
from pytsmod import ola, wsola
from pytsmod import phase_vocoder as pv
from pytsmod import phase_vocoder_int as pv_int
import soundfile as sf
import numpy as np
import os
from subprocess import call


@pytest.mark.parametrize('algorithm', ['ola', 'wsola', 'pv', 'pv_int'])
def test_console_default_params(algorithm):
test_file = 'tests/data/castanetsviolin.wav'
alpha = 2
x, sr = sf.read(test_file)
y = globals()[algorithm](x, alpha)

cmd = ['python', 'pytsmod/console.py', algorithm,
test_file, 'temp_cli.wav', str(alpha)]
if algorithm == 'pv_int':
cmd.append('-fs')
call(cmd)

sf.write('temp.wav', y, sr)
y_, _ = sf.read('temp.wav')

y_cli, _ = sf.read('temp_cli.wav')

os.remove('temp.wav')
os.remove('temp_cli.wav')

assert np.allclose(y_, y_cli)


@pytest.mark.parametrize('alpha', [1.25])
@pytest.mark.parametrize('win_type', ['sin'])
@pytest.mark.parametrize('win_size', [512])
@pytest.mark.parametrize('syn_hop_size', [256])
def test_console_ola(alpha, win_type, win_size, syn_hop_size):
test_file = 'tests/data/castanetsviolin.wav'
x, sr = sf.read(test_file)
y = ola(x, alpha, win_type=win_type, win_size=win_size,
syn_hop_size=syn_hop_size)

cmd = ['python', 'pytsmod/console.py', 'ola',
test_file, 'temp_cli.wav', str(alpha),
'-wt', win_type, '-ws', str(win_size),
'-sh', str(syn_hop_size)]
call(cmd)

sf.write('temp.wav', y, sr)
y_, _ = sf.read('temp.wav')

y_cli, _ = sf.read('temp_cli.wav')

os.remove('temp.wav')
os.remove('temp_cli.wav')

assert np.allclose(y_, y_cli)


@pytest.mark.parametrize('alpha', [1.25])
@pytest.mark.parametrize('win_type', ['sin'])
@pytest.mark.parametrize('win_size', [512])
@pytest.mark.parametrize('syn_hop_size', [256])
@pytest.mark.parametrize('tolerance', [256])
def test_console_wsola(alpha, win_type, win_size, syn_hop_size, tolerance):
test_file = 'tests/data/castanetsviolin.wav'
x, sr = sf.read(test_file)
y = wsola(x, alpha, win_type=win_type, win_size=win_size,
syn_hop_size=syn_hop_size, tolerance=tolerance)

cmd = ['python', 'pytsmod/console.py', 'wsola',
test_file, 'temp_cli.wav', str(alpha),
'-wt', win_type, '-ws', str(win_size),
'-sh', str(syn_hop_size), '-t', str(tolerance)]
call(cmd)

sf.write('temp.wav', y, sr)
y_, _ = sf.read('temp.wav')

y_cli, _ = sf.read('temp_cli.wav')

os.remove('temp.wav')
os.remove('temp_cli.wav')

assert np.allclose(y_, y_cli)


@pytest.mark.parametrize('alpha', [1.25])
@pytest.mark.parametrize('win_type', ['hann'])
@pytest.mark.parametrize('win_size', [1024])
@pytest.mark.parametrize('syn_hop_size', [256])
@pytest.mark.parametrize('zero_pad', [256])
@pytest.mark.parametrize('restore_energy', [True])
@pytest.mark.parametrize('fft_shift', [True])
@pytest.mark.parametrize('phase_lock', [True])
def test_console_pv(alpha, win_type, win_size, syn_hop_size, zero_pad,
restore_energy, fft_shift, phase_lock):
test_file = 'tests/data/castanetsviolin.wav'
x, sr = sf.read(test_file)
y = pv(x, alpha, win_type=win_type, win_size=win_size,
syn_hop_size=syn_hop_size, zero_pad=zero_pad,
restore_energy=restore_energy, fft_shift=fft_shift,
phase_lock=phase_lock)

cmd = ['python', 'pytsmod/console.py', 'pv',
test_file, 'temp_cli.wav', str(alpha),
'-wt', win_type, '-ws', str(win_size),
'-sh', str(syn_hop_size), '-z', str(zero_pad),
'-e', '-fs', '-pl']
call(cmd)

sf.write('temp.wav', y, sr)
y_, _ = sf.read('temp.wav')

y_cli, _ = sf.read('temp_cli.wav')

os.remove('temp.wav')
os.remove('temp_cli.wav')

assert np.allclose(y_, y_cli)


@pytest.mark.parametrize('alpha', [2])
@pytest.mark.parametrize('win_type', ['sin'])
@pytest.mark.parametrize('win_size', [1024])
@pytest.mark.parametrize('syn_hop_size', [256])
@pytest.mark.parametrize('zero_pad', [256])
@pytest.mark.parametrize('restore_energy', [True])
@pytest.mark.parametrize('fft_shift', [False])
def test_console_pv_int(alpha, win_type, win_size, syn_hop_size, zero_pad,
restore_energy, fft_shift):
test_file = 'tests/data/castanetsviolin.wav'
x, sr = sf.read(test_file)
y = pv(x, alpha, win_type=win_type, win_size=win_size,
syn_hop_size=syn_hop_size, zero_pad=zero_pad,
restore_energy=restore_energy, fft_shift=fft_shift)

cmd = ['python', 'pytsmod/console.py', 'pv',
test_file, 'temp_cli.wav', str(alpha),
'-wt', win_type, '-ws', str(win_size),
'-sh', str(syn_hop_size), '-z', str(zero_pad),
'-e']
call(cmd)

sf.write('temp.wav', y, sr)
y_, _ = sf.read('temp.wav')

y_cli, _ = sf.read('temp_cli.wav')

os.remove('temp.wav')
os.remove('temp_cli.wav')

assert np.allclose(y_, y_cli)