Skip to content

Instantly share code, notes, and snippets.

@rniwase
Created March 2, 2019 23:53
Show Gist options
  • Save rniwase/a360eb22f0ba8158c2bcfc2465202d8b to your computer and use it in GitHub Desktop.
Save rniwase/a360eb22f0ba8158c2bcfc2465202d8b to your computer and use it in GitHub Desktop.
自動でアーメンを刻むスクリプト、ループ・逆再生・トランスポーズ・タイムストレッチ・エンベロープ・ビットリダクションエフェクト付き
import os
import sys
import wave
import numpy as np
in_file = "amenbrother.wav" # 入力ファイル
out_filename_detail = True # 出力ファイル名にパラメータをつける
in_bar = 1 # 入力データの小節数
out_bar = 8 # 出力データの小節数
transient = 8 # 1小節の刻み数
rand_seed = None # 乱数のシード値(None:実行毎にランダム,自然数:シード値)
rand_prob = 0.2 # ランダマイズ確率(0.0~1.0)
loop_trans = 2 # ループ分割数(1:なし,n:n分割)
loop_prob = 0.2 # ループ確率(0.0~1.0)
rev_prob = 0.1 # 逆再生確率(0.0~1.0)
transpose = [-2, 1, 3, 5] # ピッチシフト(半音,複数指定可)
transpose_prob = 0. # ピッチシフト確率
stretch = 2.0 # ストレッチ(1.0以上)
stretch_prob = 0.08 # ストレッチ確率
env_attack = 0. # エンベロープ - アタック時間(0.0~1.0)
env_hold = 1. # エンベロープ - ホールド時間(0.0~1.0)
env_release = 0.1 # エンベロープ - リリース時間(0.0~1.0)
env_prob = 0.1 # エンベロープ確率
reduc_depth = 6 # リダクション - ビット深度
reduc_rate = 0.3 # リダクション - サンプルレート(0.0~1.0)
reduc_prob = 0.#0.1 # リダクション確率
# エフェクト
def timestretch(data, stretch, grain_size=800, fade=80):
grain_num = int(data.size / grain_size) + 1
grained_data = np.hstack([data, np.zeros(grain_size * grain_num - data.size).astype(dtype="int16")])
grained_data = grained_data.reshape((grain_num, grain_size))
stretched_ary = np.zeros((grain_num, int(grain_size * stretch))).astype(dtype="int16")
if stretch > 1.:
fade_weight = np.ones(grain_size)
fade_weight[:fade] *= np.linspace(0., 1., fade_weight[:fade].size)
fade_weight[-fade:] *= np.linspace(1., 0., fade_weight[-fade:].size)
for i in range(int(stretch)):
stretched_ary[:, i * grain_size:(i + 1) * grain_size] = (grained_data[:, :] * fade_weight).astype(dtype="int16")
fade_weight = np.ones(int(grain_size * (stretch % 1. + 1.)) - grain_size)
fade_weight[:fade] *= np.linspace(0., 1., fade_weight[:fade].size)
fade_weight[-fade:] *= np.linspace(1., 0., fade_weight[-fade:].size)
stretched_ary[:, int(stretch) * grain_size:] = (grained_data[:, :int(grain_size * (stretch % 1. + 1.)) - grain_size] * fade_weight).astype(dtype="int16")
elif stretch < 1.:
fade_weight = np.ones(int(grain_size * stretch))
fade_weight[:fade] *= np.linspace(0., 1., fade_weight[:fade].size)
fade_weight[-fade:] *= np.linspace(1., 0., fade_weight[-fade:].size)
stretched_ary[:, :] = (grained_data[:, :int(grain_size * stretch)] * fade_weight).astype(dtype="int16")
else:
return data
stretched_ary = stretched_ary.reshape(stretched_ary.size)
return stretched_ary
def speedx(sound_array, factor):
indices = np.round( np.arange(0, len(sound_array), factor) )
indices = indices[indices < len(sound_array)].astype(int)
return sound_array[ indices.astype(int) ]
def pitchshift(data, semitones, grain_size=800, fade=80):
factor = 2**(1.0 * semitones / 12.0)
# resampled = scipy.signal.resample(data, int(data.size / factor)).astype("int16")
# stretched = timestretch(resampled, factor, grain_size=grain_size, fade=fade)
resampled = speedx(data, factor).astype("int16")
stretched = timestretch(resampled, factor, grain_size=grain_size, fade=fade)
return stretched[:data.size]
def reduction(data, depth, rate):
return (speedx(speedx(data, 1. / rate), rate)[:data.size] & (0xFFFF << depth)).astype("int16")
in_data = wave.open(in_file, "r")
wave_ary = in_data.readframes(in_data.getnframes())
wave_ary = np.frombuffer(wave_ary, dtype="int16")
transient *= in_bar
out_bar = int(out_bar / in_bar)
crp_len = int(wave_ary.size / transient)
if rand_seed == None:
rand_seed = np.random.randint(0, 2**31)
np.random.seed(seed=rand_seed)
random_ary = np.random.randint(0, transient, transient * out_bar)
orig_ary = np.array([i for i in range(transient)] * out_bar)
rand_prob_ary = np.array(np.random.rand(transient * out_bar)
< rand_prob).astype(int)
seq_ary = (rand_prob_ary * random_ary) + ((-rand_prob_ary + 1) * orig_ary)
loop_prob_ary = np.array(np.random.rand(transient * out_bar)
< loop_prob).astype(int)
loop_crp_len = int(crp_len / loop_trans)
rev_prob_ary = np.array(np.random.rand(transient * out_bar)
< rev_prob).astype(int)
trps_prob_ary = np.array(np.random.rand(transient * out_bar)
< transpose_prob).astype(int)
trps_prob_ary *= np.random.randint(1, len(transpose) + 1, trps_prob_ary.size)
strc_prob_ary = np.array(np.random.rand(transient * out_bar)
< stretch_prob).astype(int)
env_prob_ary = np.array(np.random.rand(transient * out_bar)
< env_prob).astype(int)
env_attack_len = int(env_attack * crp_len)
env_hold_len = int(env_hold * crp_len)
env_release_len = int(env_release * crp_len)
env_weight = np.zeros(crp_len)
env_weight[:env_hold_len] += 1.
env_weight[:env_attack_len] *= np.linspace(0., 1., env_weight[:env_attack_len].size)
env_weight[env_hold_len-env_release_len:env_hold_len] *= np.linspace(1., 0., env_weight[env_hold_len-env_release_len:env_hold_len].size)
reduc_prob_ary = np.array(np.random.rand(transient * out_bar)
< reduc_prob).astype(int)
com_ary = np.array([seq_ary,
loop_prob_ary,
rev_prob_ary,
trps_prob_ary,
strc_prob_ary,
env_prob_ary,
reduc_prob_ary]).T
print(com_ary)
params = (os.path.split(in_file)[1],
in_bar,
transient,
out_bar,
rand_seed,
rand_prob,
loop_trans,
loop_prob,
rev_prob,
transpose,
transpose_prob,
stretch,
stretch_prob,
env_attack,
env_hold,
env_release,
env_prob,
reduc_depth,
reduc_rate,
reduc_prob
)
print(params)
try:
if out_filename_detail:
out_data = wave.Wave_write("out_{0}.wav".format(params))
else:
out_data = wave.Wave_write("out.wav")
except PermissionError:
print("ファイルが使用中もしくは書き込む権限がありません.")
sys.exit()
out_data.setparams(in_data.getparams())
for a in com_ary:
seq_data = wave_ary[a[0] * crp_len:(a[0] + 1) * crp_len]
# エフェクトの順序
if a[3] != 0: # ピッチシフト
seq_data = pitchshift(seq_data, transpose[a[3] - 1])
if a[4] != 0: # タイムストレッチ
seq_data = timestretch(seq_data, stretch)[:seq_data.size]
if a[5] != 0: # エンベロープ
seq_data = (seq_data * env_weight).astype("int16")
if a[2] != 0: # 逆再生
seq_data = np.array(seq_data[::-1])
if a[1] != 0: # ループ
seq_data = np.hstack([seq_data[:loop_crp_len]] * loop_trans)
if a[6] != 0: # リダクション
seq_data = reduction(seq_data, reduc_depth, reduc_rate)
out_data.writeframes(seq_data)
out_data.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment