-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4472e6f
commit 549d49c
Showing
10 changed files
with
333 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from pydantic import BaseModel | ||
|
||
from libresvip.model.option_mixins import ( | ||
EnableInstrumentalTrackImportationMixin, | ||
EnablePitchImportationMixin, | ||
ExtractEmbededAudioMixin, | ||
StaticTempoMixin, | ||
) | ||
|
||
|
||
class InputOptions( | ||
EnableInstrumentalTrackImportationMixin, | ||
EnablePitchImportationMixin, | ||
ExtractEmbededAudioMixin, | ||
BaseModel, | ||
): | ||
pass | ||
|
||
|
||
class OutputOptions(StaticTempoMixin, BaseModel): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[Core] | ||
name = VOX Factory | ||
module = vox_factory_converter | ||
|
||
[Documentation] | ||
Author = SoulMelody | ||
Version = 1.0.0 | ||
Website = https://space.bilibili.com/175862486 | ||
Format = VOX Factory Project File | ||
Description = Conversion plugin for VOX Factory Project File | ||
Suffix = vfp | ||
IconBase64 = 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import io | ||
import pathlib | ||
import zipfile | ||
|
||
from libresvip.core.compat import json | ||
from libresvip.extension import base as plugin_base | ||
from libresvip.model.base import Project | ||
|
||
from .model import VOXFactoryProject | ||
from .options import InputOptions, OutputOptions | ||
from .vox_factory_generator import VOXFactoryGenerator | ||
from .vox_factory_parser import VOXFactoryParser | ||
|
||
|
||
class VOXFactoryConverter(plugin_base.SVSConverterBase): | ||
def load(self, path: pathlib.Path, options: InputOptions) -> Project: | ||
with zipfile.ZipFile(io.BytesIO(path.read_bytes()), "r") as archive_file: | ||
proj = VOXFactoryProject.model_validate_json( | ||
archive_file.read("project.json"), | ||
context={ | ||
"extract_audio": options.extract_audio, | ||
"path": path, | ||
"archive_file": archive_file, | ||
}, | ||
) | ||
return VOXFactoryParser(options, path).parse_project(proj) | ||
|
||
def dump(self, path: pathlib.Path, project: Project, options: OutputOptions) -> None: | ||
buffer = io.BytesIO() | ||
generator = VOXFactoryGenerator(options) | ||
vox_factory_project = generator.generate_project(project) | ||
with zipfile.ZipFile(buffer, "w") as archive_file: | ||
archive_file.writestr( | ||
"project.json", | ||
json.dumps( | ||
vox_factory_project.model_dump(mode="json", exclude_none=True, by_alias=True), | ||
ensure_ascii=False, | ||
), | ||
) | ||
for audio_name, audio_path in generator.audio_paths.items(): | ||
archive_file.writestr(f"resources/{audio_name}", audio_path.read_bytes()) | ||
path.write_bytes(buffer.getvalue()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import dataclasses | ||
import pathlib | ||
|
||
from libresvip.core.constants import DEFAULT_BPM | ||
from libresvip.model.base import Note, Project, SingingTrack, SongTempo, TimeSignature, Track | ||
|
||
from .model import ( | ||
VOXFactoryNote, | ||
VOXFactoryProject, | ||
VOXFactoryTrack, | ||
VOXFactoryVocalClip, | ||
VOXFactoryVocalTrack, | ||
) | ||
from .options import OutputOptions | ||
|
||
|
||
@dataclasses.dataclass | ||
class VOXFactoryGenerator: | ||
options: OutputOptions | ||
audio_paths: dict[str, pathlib.Path] = dataclasses.field(default_factory=dict) | ||
|
||
def generate_project(self, project: Project) -> VOXFactoryProject: | ||
vox_project = VOXFactoryProject( | ||
tempo=self.generate_tempo(project.song_tempo_list), | ||
time_signature=self.generate_time_signature(project.time_signature_list), | ||
track_bank=self.generate_tracks(project.track_list), | ||
) | ||
vox_project.track_order = sorted(vox_project.track_bank.keys()) | ||
return vox_project | ||
|
||
def generate_tempo(self, tempos: list[SongTempo]) -> float: | ||
return tempos[0].bpm if tempos else DEFAULT_BPM | ||
|
||
def generate_time_signature(self, time_signatures: list[TimeSignature]) -> list[int]: | ||
if time_signatures: | ||
return [time_signatures[0].numerator, time_signatures[0].denominator] | ||
else: | ||
return [4, 4] | ||
|
||
def generate_tracks(self, tracks: list[Track]) -> dict[str, VOXFactoryTrack]: | ||
track_bank = {} | ||
for i, track in enumerate(tracks): | ||
if isinstance(track, SingingTrack): | ||
note_list = self.generate_notes(track.note_list) | ||
clip_bank = {f"clip_{i}": clip for i, clip in enumerate(note_list)} | ||
clip_order = sorted(clip_bank.keys()) | ||
track_bank[str(i)] = VOXFactoryVocalTrack( | ||
clip_bank=clip_bank, | ||
clip_order=clip_order, | ||
) | ||
return track_bank | ||
|
||
def generate_notes(self, notes: list[Note]) -> dict[str, VOXFactoryVocalClip]: | ||
note_bank = {} | ||
note_order = [] | ||
for i, note in enumerate(notes): | ||
note_bank[f"note_{i}"] = self.generate_note(note) | ||
note_order.append(f"note_{i}") | ||
return { | ||
"clip": VOXFactoryVocalClip( | ||
note_bank=note_bank, | ||
note_order=note_order, | ||
), | ||
} | ||
|
||
def generate_note(self, note: Note) -> VOXFactoryNote: | ||
return VOXFactoryNote( | ||
ticks=note.start_pos, | ||
duration_ticks=note.length, | ||
midi=note.key_number, | ||
name=note.lyric, | ||
syllable=note.pronunciation, | ||
) |
Oops, something went wrong.