Skip to content

Commit

Permalink
vfp: update pitch parser
Browse files Browse the repository at this point in the history
  • Loading branch information
SoulMelody committed Jan 7, 2025
1 parent ae2069c commit 6616c6f
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 38 deletions.
2 changes: 2 additions & 0 deletions libresvip/plugins/ps_project/pocket_singer_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ def parse_singing_tracks(self) -> list[SingingTrack]:
note_list=notes,
)
if self.options.import_pitch:
pitch_points.insert(0, Point.start_point())
pitch_points.append(Point.end_point())
singing_track.edited_params.pitch.points.root = pitch_points
singing_tracks.append(singing_track)
return singing_tracks
Expand Down
2 changes: 1 addition & 1 deletion libresvip/plugins/vfp/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class VOXFactoryNote(BaseModel):
duration: float
duration_ticks: float = Field(alias="durationTicks")
velocity: int
note_type: None = Field(alias="noteType")
note_type: Optional[str] = Field(None, alias="noteType")
vibrato_depth: Optional[float] = Field(None, alias="vibratoDepth")
pre_bend: Optional[float] = Field(None, alias="preBend")
post_bend: Optional[float] = Field(None, alias="postBend")
Expand Down
112 changes: 75 additions & 37 deletions libresvip/plugins/vfp/vox_factory_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import dataclasses
import pathlib

from libresvip.core.constants import TICKS_IN_BEAT
from libresvip.core.time_sync import TimeSynchronizer
from libresvip.model.base import (
InstrumentalTrack,
Note,
Expand All @@ -10,10 +12,10 @@
TimeSignature,
Track,
)
from libresvip.model.point import Point

from .model import (
VOXFactoryAudioTrack,
VOXFactoryNote,
VOXFactoryProject,
VOXFactoryVocalClip,
)
Expand All @@ -24,6 +26,8 @@
class VOXFactoryParser:
options: InputOptions
path: pathlib.Path
synchronizer: TimeSynchronizer = dataclasses.field(init=False)
first_bar_length: int = dataclasses.field(init=False)

def parse_project(self, vox_project: VOXFactoryProject) -> Project:
project = Project(
Expand All @@ -34,21 +38,23 @@ def parse_project(self, vox_project: VOXFactoryProject) -> Project:
return project

def parse_tempos(self, tempo: float) -> list[SongTempo]:
return [
tempos = [
SongTempo(
bpm=tempo,
position=0,
)
]
self.synchronizer = TimeSynchronizer(tempos)
return tempos

def parse_time_signatures(self, time_signatures: list[int]) -> list[TimeSignature]:
return [
TimeSignature(
bar_index=0,
numerator=time_signatures[0],
denominator=time_signatures[1],
)
]
time_signature = TimeSignature(
bar_index=0,
numerator=time_signatures[0],
denominator=time_signatures[1],
)
self.first_bar_length = int(time_signature.bar_length())
return [time_signature]

def parse_tracks(self, vox_project: VOXFactoryProject) -> list[Track]:
tracks = []
Expand All @@ -62,25 +68,35 @@ def parse_tracks(self, vox_project: VOXFactoryProject) -> list[Track]:
track_data.clip_bank.items(),
key=lambda x: track_data.clip_order.index(x[0]),
):
track = InstrumentalTrack(
title=clip_data.name,
solo=track_data.solo,
mute=track_data.mute,
pan=track_data.pan,
audio_file_path=str(
(self.path.parent / clip_data.name).with_suffix(
pathlib.Path(clip_data.source_audio_data_key).suffix
)
),
audio_path = (self.path.parent / clip_data.name).with_suffix(
pathlib.Path(clip_data.source_audio_data_key).suffix
)
tracks.append(track)
if audio_path.exists():
track = InstrumentalTrack(
title=clip_data.name,
solo=track_data.solo,
mute=track_data.mute,
pan=track_data.pan,
offset=int(
TICKS_IN_BEAT
* (clip_data.start_quarter - clip_data.offset_quarter)
),
audio_file_path="",
)
tracks.append(track)
else:
note_list = []
pitch_points = []
for _, clip_data in sorted(
track_data.clip_bank.items(),
key=lambda x: track_data.clip_order.index(x[0]),
):
note_list.extend(self.parse_notes(clip_data))
clip_offset = int(
TICKS_IN_BEAT * (clip_data.start_quarter - clip_data.offset_quarter)
)
clip_notes, clip_pitch_points = self.parse_notes(clip_data, clip_offset)
note_list.extend(clip_notes)
pitch_points.extend(clip_pitch_points)
if not note_list:
continue
track = SingingTrack(
Expand All @@ -90,23 +106,45 @@ def parse_tracks(self, vox_project: VOXFactoryProject) -> list[Track]:
pan=track_data.pan,
note_list=note_list,
)
if self.options.import_pitch:
pitch_points.insert(0, Point.start_point())
pitch_points.append(Point.end_point())
track.edited_params.pitch.points.root = pitch_points
tracks.append(track)
return tracks

def parse_notes(self, clip_data: VOXFactoryVocalClip) -> list[Note]:
return [
self.parse_note(note_data)
for _, note_data in sorted(
clip_data.note_bank.items(),
key=lambda x: clip_data.note_order.index(x[0]),
def parse_notes(
self, clip_data: VOXFactoryVocalClip, clip_offset: int
) -> tuple[list[Note], list[Point]]:
notes = []
pitch_points: list[Point] = []
f0_secs_step = 1024 / 44100
for _, note_data in sorted(
clip_data.note_bank.items(),
key=lambda x: clip_data.note_order.index(x[0]),
):
note = Note(
start_pos=int(note_data.ticks + clip_offset),
length=int(note_data.duration_ticks),
key_number=note_data.midi,
lyric=note_data.name,
pronunciation=note_data.syllable,
)
]

def parse_note(self, note_data: VOXFactoryNote) -> Note:
return Note(
start_pos=int(note_data.ticks),
length=int(note_data.duration_ticks),
key_number=note_data.midi,
lyric=note_data.name,
pronunciation=note_data.syllable,
)
if self.options.import_pitch and note_data.pitch_bends:
pitch_start = int(note_data.ticks + clip_offset + (note_data.pre_bend or 0))
note_pitch_points = [Point(x=pitch_start + self.first_bar_length, y=-100)]
pitch_start_secs = self.synchronizer.get_actual_secs_from_ticks(pitch_start)
pitch_end_secs = pitch_start_secs + f0_secs_step * len(note_data.pitch_bends)
pitch_end = self.synchronizer.get_actual_ticks_from_secs(pitch_end_secs)
pitch_step = (pitch_end - pitch_start) / len(note_data.pitch_bends)
for i, pitch_bend in enumerate(note_data.pitch_bends):
note_pitch_points.append(
Point(
x=int(pitch_start + i * pitch_step) + self.first_bar_length,
y=int((note_data.midi + pitch_bend) * 100),
)
)
note_pitch_points.append(Point(x=int(pitch_end) + self.first_bar_length, y=-100))
pitch_points.extend(note_pitch_points)
notes.append(note)
return notes, pitch_points

0 comments on commit 6616c6f

Please sign in to comment.