Spaces:
No application file
No application file
import logging | |
from .music_clip import MusicClip, MusicClipSeq | |
from .music_map import MusicMap | |
from ...data.clip.clip_process import find_idx_by_time | |
logger = logging.getLogger(__name__) # pylint: disable=invalid-name | |
def insert_mss_2_clipseq( | |
clipseq: MusicClipSeq, mss_clipseq: MusicClipSeq | |
) -> MusicClipSeq: | |
"""将mss中的结构字段信息赋予到目标clipseq中的最近clip | |
Args: | |
clipseq (ClipSeq): 目标clip序列 | |
reference (ClipSeq): 参考clip序列 | |
field (str): 目标字段 | |
Returns: | |
ClipSeq: 更新目标字段新值后的clip序列 | |
""" | |
for i, clip in enumerate(clipseq): | |
idx = find_idx_by_time(mss_clipseq, clip.time_start) | |
if idx is not None: | |
clipseq[i].stage = mss_clipseq[idx].stage | |
else: | |
clipseq[i].stage = "unknow" | |
return clipseq | |
def get_mss_musicinfo(songid: str) -> MusicMap: | |
"""通过调用media_data中的接口 获取天琴实验室的歌曲结构信息 | |
Args: | |
songid (str): 歌词id | |
Returns: | |
MusicMap: mss结构信息生成的音乐谱面 | |
""" | |
try: | |
from media_data.oi.tianqin_database import get_mss | |
mss = get_mss(songid=songid) | |
except Exception as e: | |
logger.warning("get mss failed, mss={}".format(songid)) | |
logger.exception(e) | |
mss = None | |
mss_musicinfo = MusicMap(mss) if mss is not None else None | |
return mss_musicinfo | |
def merge_mss(musicinfo: MusicMap, mss: MusicMap) -> MusicMap: | |
"""融合mss音乐谱面到目标音乐谱面 | |
Args: | |
musicinfo (MusicMap): 目标音乐谱面 | |
mss (MusicMap): 待融合的mss音乐谱面 | |
Returns: | |
MusicMap: 融合后的音乐谱面 | |
""" | |
musicinfo.meta_info.bpm = mss.meta_info.bpm | |
if len(mss.clipseq) > 0: | |
musicinfo.clipseq = insert_mss_2_clipseq(musicinfo.clipseq, mss.clipseq) | |
return musicinfo | |
def generate_mss_from_lyric(lyrics: list, audio_duration: float, th=8) -> MusicClipSeq: | |
# "intro", "VA", "CA", "bridge", "VB", "CB", "end"] | |
mss = [] | |
n_lyric = len(lyrics) | |
for lyric_idx, line_lyric_dct in enumerate(lyrics): | |
time_start = line_lyric_dct["time_start"] | |
duration = line_lyric_dct["duration"] | |
time_end = time_start + duration | |
# text = line_lyric_dct["text"] | |
if lyric_idx == 0: | |
sub_mss = { | |
"stage": "intro", | |
"time_start": 0, | |
"duration": time_start, | |
} | |
mss.append(sub_mss) | |
continue | |
if lyric_idx == n_lyric - 1: | |
sub_mss = { | |
"stage": "end", | |
"time_start": time_end, | |
"duration": audio_duration - time_end, | |
} | |
mss.append(sub_mss) | |
continue | |
if lyrics[lyric_idx + 1]["time_start"] - time_end >= th: | |
sub_mss = { | |
"stage": "bridge", | |
"time_start": time_end, | |
"duration": lyrics[lyric_idx + 1]["time_start"] - time_end, | |
} | |
mss.append(sub_mss) | |
mss_lyric = [] | |
for sub_idx, sub_mss in enumerate(mss): | |
if sub_idx == len(mss) - 1: | |
continue | |
time_end = sub_mss["time_start"] + sub_mss["duration"] | |
next_time_start = mss[sub_idx + 1]["time_start"] | |
if next_time_start - time_end > 0.1: | |
mss_lyric.append( | |
{ | |
"stage": "lyric", | |
"time_start": time_end, | |
"duration": next_time_start - time_end, | |
} | |
) | |
mss.extend(mss_lyric) | |
mss = sorted(mss, key=lambda x: x["time_start"]) | |
mss = MusicClipSeq(mss) | |
return mss | |
def refine_mss_info_from_tianqin( | |
mss_info: MusicMap, lyricseq: MusicClipSeq | |
) -> MusicMap: | |
"""优化天琴的歌曲结信息, | |
优化前:天琴歌曲结构里面只有每句歌词和结构信息,时间前后不连续,对于整首歌去时间结构不完备。 | |
优化后:增加intro,bridge,end,将相近的结构信息合并,时间前后连续,时间完备 | |
Args: | |
mss_info (MusicMap): 天琴歌曲结构 | |
lyricseq (ClipSeq): 原始歌曲信息,用于计算Intro,bridge,end。其实也可以从mss_info中获取。 | |
Returns: | |
MusicMap: 优化后的歌曲结构信息 | |
""" | |
lyric_mss_clipseq = generate_mss_from_lyric( | |
lyricseq, audio_duration=mss_info.meta_info.duration | |
) | |
new_mss_clipseq = [] | |
# lyric_mss_dct = lyric_mss_clipseq.to_dct() | |
# mss_dct = mss_info.clipseq.to_dct() | |
for l_clip_idx, lyric_clip in enumerate(lyric_mss_clipseq): | |
if lyric_clip.stage != "lyric": | |
new_mss_clipseq.append(lyric_clip) | |
else: | |
new_clip_time_start = lyric_clip.time_start | |
last_stage = "ANewClipStart" | |
for clip_idx, clip in enumerate(mss_info.clipseq): | |
if clip.time_start < new_clip_time_start: | |
continue | |
if ( | |
clip.time_start >= lyric_mss_clipseq[l_clip_idx + 1].time_start | |
or clip_idx == len(mss_info.clipseq) - 1 | |
): | |
if clip.time_start >= lyric_mss_clipseq[l_clip_idx + 1].time_start: | |
stage = last_stage | |
# 像偶阵雨这首歌最后一个歌词段落 只有一句歌词 | |
if clip_idx == len(mss_info.clipseq) - 1: | |
stage = clip.stage | |
new_clip_time_end = lyric_mss_clipseq[l_clip_idx + 1].time_start | |
new_stage_clip = { | |
"time_start": new_clip_time_start, | |
"duration": new_clip_time_end - new_clip_time_start, | |
"stage": stage, | |
} | |
new_mss_clipseq.append(MusicClip(**new_stage_clip)) | |
new_clip_time_start = new_clip_time_end | |
last_stage = clip.stage | |
break | |
if clip.stage != last_stage: | |
if last_stage == "ANewClipStart": | |
last_stage = clip.stage | |
continue | |
new_clip_time_end = mss_info.clipseq[clip_idx].time_start | |
new_stage_clip = { | |
"time_start": new_clip_time_start, | |
"duration": new_clip_time_end - new_clip_time_start, | |
"stage": last_stage, | |
} | |
new_mss_clipseq.append(MusicClip(**new_stage_clip)) | |
new_clip_time_start = new_clip_time_end | |
last_stage = clip.stage | |
new_mss_clipseq = MusicClipSeq(sorted(new_mss_clipseq, key=lambda x: x.time_start)) | |
mss_info.clipseq = new_mss_clipseq | |
return mss_info | |