Skip to content

Commit

Permalink
Release RC version
Browse files Browse the repository at this point in the history
  • Loading branch information
Sg4Dylan committed Dec 26, 2018
1 parent 48d6b68 commit a78e4f2
Show file tree
Hide file tree
Showing 11 changed files with 1,188 additions and 79 deletions.
74 changes: 0 additions & 74 deletions EmiyaEngine.py

This file was deleted.

121 changes: 116 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,122 @@
Emiya Engine 是一个用来丰富音频频谱的脚本。可以将频谱变得好看那么一点。

### 使用说明
当前分支使用的技术类似于大法的 DSEE HX,透过频带拷贝增强效果。
效果虽好,但需要使用者具有一定的观察能力。
请务必阅读完程序内参数注释再使用。
不同于原版或 AkkoMode,当前分支版本伴随的噪声更小。
---

### 当前版本:

`RC Version 0`

### 编年史:

- `Alpha.0 Rev.3`
> 这算是 Emiya Engine 的第一个阶段成果,目标的最小实现
> 简单说原理就是矩形窗暴力 FFT,移频,乘以乱数,叠加,IFFT
> 大部分的代码是为了处理超大数组拼接速度缓慢的问题
> 处理后的音频有大量爆音及咔哒声,低电平音频容易看出处理痕迹
- `Alpha.1 Rev.0`
> 该版本为 Alpha.0 的重构改进,主要工作是改写为多进程执行
> 为消除频谱图上可见的断层,加入了整数倍时域重采样机制
> 事实上重采样带来的运算增加远超多进程带来的提升,所以...
> 以及因为多进程,处理需要占用更大的内存,性能消耗巨大
> 爆音和咔哒声依旧存在,但已大幅减少,处理痕迹依旧能看出来
- `Alpha.1 Rev.1`
> 这一版中加入了 AkkoMode
> 这一模式原理极其简单,就是给原始信号采样点分别乘以极小的随机数
> 可以视作信号在有微小热噪声的线路走了一趟
> 处理后无爆音及咔哒声,但在低电平音频上能听出背景噪声
> 消除背景噪声就必须暴露处理痕迹
- `Alpha.3`
> 推翻了之前的所有代码的完全重构,处理结果类似 DSEE HX
> 这一版本质是高通滤波器 + 混频器
> 将高通滤波后的信号分离为打击乐及弦乐,然后增益后叠加在原始信号上
> 丢掉了自造的 FFT 轮子,改用库实现的 SFFT
> 因此不存在爆音和咔哒声,也不再需要额外多倍重采样,速度极大提升
> 这一版本参数调节极其重要,需要参照结果反复调整参数
> 正确调整参数的处理样本完全不增加爆音及咔哒声,加上 EQ 能完全抹平处理痕迹
### 当前版本使用说明:

为了方便使用,特地做了个 GUI 界面,
但实际上还是挺难用的,所以还是说一下。

工具: Spek(仅频谱观察用),Audition(频谱观察/频率分析/后期处理用)

首先需要分析音乐类型,对于以下类型不建议使用 AkkoMode:
- 电子合成纯音乐,背景乐器只有一两样的
- 人声清唱带一个伴奏乐器
- 其他频谱图中最高频率不到 18kHz 的音乐

例如这样的:
![sample-not-for-akkomode](https://i.imgur.com/Fd4EoGN.jpg?1)

AkkoMode 适用于大部分时候音量都很大的流行乐(比如 JPOP),
处理时应选用 Apple iTunes 购买的 AAC 格式音频,常见的频谱长这样:
![some-jpop](https://i.imgur.com/swdtDz6.jpg)
因为参数只有俩,调整并不麻烦,此处就不展开说了。

CopyBand 模式需要设置六个参数,配置之前观察频谱。
以某网站下载的音乐为例,以下是其频谱图及频率分析图:
![sample-spec-0](https://i.imgur.com/RzEzmtl.jpg)
![sample-spec-1](https://i.imgur.com/t0ps5iS.png)

从两张图中可以明显看出频率在 17kHz 不到的地方戛然而止,
如果目标是生成 48kHz 文件,则需要补齐 24-17=7kHz 的部分。
而 17-7=10kHz,故 HPF 截止频率应设定在 10kHz 以下,
而调制频率则在 HPF 截止频率上加上 7k。
本例中设定为 9k 及 16k。
这首歌背景音乐以打击乐为主,因此能量集中在冲击部分,
调参数时,首先将谐波增益设置为 0,可以避免参数过多干扰测试。
冲击增益可以从 5 开始测试,勾上测试模式,启动输出,
检查输出文件频率分析结果:
![sample-result-0](https://i.imgur.com/gqBmSFy.png)

很明显,在 17-21kHz 的地方本应该是比 17kHz 以下的部分“矮”一些的。
(高频衰减更大,所以高频部分通常增益应低于低频)
因此,根据观察结果,将冲击增益调为 2.5(折半试错),再重新跑一次。
(此时要在其他软件中关闭文件,否则会发生错误)
调整后的频率分析结果变成了这样:
![sample-result-1](https://i.imgur.com/aDempeR.png)

此时已经很接近理想的样子了,因为还要加入谐波的部分(前边设定成了 0)
故再将冲击增益降低 0.5,同时给谐波增益改为 1.0 并再次执行。
结果变成了这样:
![sample-result-2](https://i.imgur.com/cAqZdbQ.png)
看起来不错,直接取消测试模式生成最终结果。
生成最终结果时可能会很卡,请不要担心并耐心等待,进度条将滚动四次(两声道音频)。

接着检查频谱,输出如下:
![sample-result-3](https://i.imgur.com/E5I1fMf.jpg)
![sample-result-4](https://i.imgur.com/pFZDbHB.png)
此时是不是有点失望了,很明显的衔接痕迹对不对。

没关系,这时可以打开 Audition 效果中的 FFT 滤波器,
接着拿起刚才的频率分析结果图,照着图调整 FFT 滤波器,比如这样:
![au-fft-filter](https://i.imgur.com/dbHxIKH.png)
应用后,频率分析结果变成了这样:
![final-0](https://i.imgur.com/9eYJs8V.png)
而频谱中的衔接痕迹已经不明显了:
![final-1](https://i.imgur.com/X1cDgcX.jpg)

放大频谱细节,可以看出雾蒙蒙的部分依然有欠缺,
![final-2](https://i.imgur.com/9AbW9j2.jpg)
这是谐波增益不够的原因,可以继续调整改善。最终得到以下结果:
![final-3](https://i.imgur.com/2UO9OnW.jpg)

### 其他提示:

由于 CopyBand 本质是复制粘贴已有的部分,
因此对于超过 48kHz 以上的拉升,需要多次处理达成,
例如以下原始文件不到 16kHz:
![ex-0](https://i.imgur.com/eAui0i7.jpg)
拉升到 48kHz 采样需要的频率片段至少为 24-16=8kHz,
而拉升到 96kHz 采样则需要 48-16=32kHz。
而原始音频中都没有 32kHz 的容量,
因此在最终拉升到 96kHz 之前需要重复至少三次操作。
在这一过程中,最大频率由 16 最终变为 48Hz。
由于事实上 20kHz 以上的听不见,所以你做得再多也无妨(笑)。
例如上边的例子被拉升到 192kHz 采样率,宛如天籁之声:
![ex-1](https://i.imgur.com/QWKpaHA.jpg)

### 特别提醒
~~请不要使用这个脚本制造 `'HiRes'` 逗玄学家玩~~
Expand Down
6 changes: 6 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ui": "res/window.ui",
"lang": "chs",
"eng": "res/eng.qm",
"chs": "res/chs.ts"
}
61 changes: 61 additions & 0 deletions core/akkomode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import random
import librosa
import resampy
#from tqdm import tqdm


def core(
input_path,output_path,
output_sr=48000,inter_sr=1,
test_mode=False,
sv_l=0.02,sv_h=0.55,
update=None
):

# 加载音频
y, sr = librosa.load(input_path,mono=False,sr=None)
if test_mode:
y, sr = librosa.load(input_path,mono=False,sr=None,offset=round(len(y[0])/sr/2),duration=5)
y = resampy.resample(y, sr, output_sr * inter_sr, filter='kaiser_fast')

# AkkoMode
for chan in y:
# 是否第一次执行
is_loop_once = True
# 前一次的数值
pre_value = 0
# 前一次操作的数值
pre_opt = 0
# 实际操作
#for i in tqdm(range(len(chan)),unit='Segment',ascii=True):
for i in range(len(chan)):
update.emit(i/len(chan))
this_value = chan[i]
# 构造抖动值
linear_jitter = 0
if pre_value < this_value:
linear_jitter = random.uniform(this_value*-sv_l, this_value*sv_h)
else:
linear_jitter = random.uniform(this_value*sv_h, this_value*-sv_l)
# 应用抖动
if pre_opt*linear_jitter > 0:
chan[i] = this_value + linear_jitter
elif pre_opt*linear_jitter < 0:
chan[i] = this_value - linear_jitter
else:
pass
# 第一次操作特殊化处理
if is_loop_once:
linear_jitter = random.uniform(this_value*-sv_h, this_value*sv_h)
chan[i] += linear_jitter
is_loop_once = False
# 保存到上一次记录
pre_value = this_value
pre_opt = linear_jitter

# 合并输出
final_data = resampy.resample(y,
output_sr * inter_sr,
output_sr,
filter='kaiser_fast')
librosa.output.write_wav(output_path, final_data, output_sr)
57 changes: 57 additions & 0 deletions core/copyband.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import numpy as np
import scipy.signal as signal
import librosa
import resampy
#from tqdm import tqdm


def core(
input_path,output_path,
output_sr=48000,inter_sr=1,
test_mode=False,
harmonic_hpfc=6000,harmonic_sft=16000,harmonic_gain=1.2,
percussive_hpfc=6000,percussive_stf=16000,percussive_gain=2.5,
update=None
):

def hpd_n_shift(data, lpf, sft, gain):
# 高通滤波
b,a = signal.butter(3,lpf/(sr/2),'high')
data = librosa.stft(signal.filtfilt(b,a,librosa.istft(data)))
# 拷贝频谱
#for i in tqdm(range(data.shape[1]),unit='Segment',ascii=True):
for i in range(data.shape[1]):
update.emit(i/data.shape[1])
shift = sft
shift_point = round(shift/(sr/data.shape[0]))
# 调制
for p in reversed(range(len(chan[:,i]))):
data[:,i][p] = data[:,i][p-shift_point]
# 高通滤波
data = librosa.stft(signal.filtfilt(b,a,librosa.istft(data)))
data *= gain
return data

# 加载音频
y, sr = librosa.load(input_path,mono=False,sr=None)
if test_mode:
y, sr = librosa.load(input_path,mono=False,sr=None,offset=round(len(y[0])/sr/2),duration=5)
y = resampy.resample(y, sr, output_sr * inter_sr, filter='kaiser_fast')
# 产生 STFT 谱
stft_list = [librosa.stft(chan) for chan in y]

# 谐波增强模式
for chan in stft_list:
D_harmonic,D_percussive = librosa.decompose.hpss(chan, margin=4)
D_harmonic = hpd_n_shift(D_harmonic,harmonic_hpfc,harmonic_sft,harmonic_gain)
D_percussive = hpd_n_shift(D_percussive,percussive_hpfc,percussive_stf,percussive_gain)
chan += D_harmonic
chan += D_percussive

# 合并输出
istft_list = [librosa.istft(chan) for chan in stft_list]
final_data = resampy.resample(np.array(istft_list),
output_sr * inter_sr,
output_sr,
filter='kaiser_fast')
librosa.output.write_wav(output_path, final_data, output_sr)
Loading

0 comments on commit a78e4f2

Please sign in to comment.