mirror of
https://github.com/TriM-Organization/Musicreater.git
synced 2026-04-17 06:08:00 +00:00
完美,同志,完美!!!!!!!
This commit is contained in:
@@ -27,7 +27,6 @@ https://gitee.com/TriM-Organization/Musicreater/blob/master/LICENSE.md。
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
|
||||
|
||||
__version__ = "3.0.0-alpha"
|
||||
|
||||
__author__ = (
|
||||
@@ -36,3 +35,37 @@ __author__ = (
|
||||
("鱼旧梦", "ElapsingDreams"),
|
||||
("偷吃不是Touch", "Touch"),
|
||||
)
|
||||
|
||||
from .paramcurve import ParamCurve, InterpolationMethod, BoundaryBehaviour
|
||||
|
||||
from .data import (
|
||||
SingleMusic,
|
||||
SingleTrack,
|
||||
SingleNote,
|
||||
SoundAtmos,
|
||||
MineNote,
|
||||
CurvableParam,
|
||||
)
|
||||
|
||||
from .plugins import load_plugin_module
|
||||
|
||||
from .main import MusiCreater
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
"__author__",
|
||||
# 参数曲线相关
|
||||
"ParamCurve",
|
||||
"InterpolationMethod",
|
||||
"BoundaryBehaviour",
|
||||
# 音乐数据结构
|
||||
"SingleMusic",
|
||||
"SingleTrack",
|
||||
"SingleNote",
|
||||
"SoundAtmos",
|
||||
"MineNote",
|
||||
"CurvableParam",
|
||||
# 工程项目相关
|
||||
"load_plugin_module",
|
||||
"MusiCreater",
|
||||
]
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿
|
||||
Copyright © 2025 Eilles
|
||||
版权所有 © 2026 金羿
|
||||
Copyright © 2026 Eilles
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -26,7 +26,7 @@ Terms & Conditions: License.md in the root directory
|
||||
import sys
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
@@ -137,9 +137,12 @@ class PluginConfig(ABC):
|
||||
class PluginType(str, Enum):
|
||||
"""插件类型枚举"""
|
||||
|
||||
FUNCTION_IMPORT = "import_data"
|
||||
FUNCTION_EXPORT = "export_data"
|
||||
FUNCTION_OPERATE = "data_operate"
|
||||
FUNCTION_MUSIC_IMPORT = "import_music_data"
|
||||
FUNCTION_TRACK_IMPORT = "import_track_data"
|
||||
FUNCTION_MUSIC_OPERATE = "music_data_operating"
|
||||
FUNCTION_TRACK_OPERATE = "track_data_operating"
|
||||
FUNCTION_MUSIC_EXPORT = "export_music_data"
|
||||
FUNCTION_TRACK_EXPORT = "export_track_data"
|
||||
SERVICE = "service"
|
||||
LIBRARY = "library"
|
||||
|
||||
@@ -160,11 +163,11 @@ class PluginMetaInformation(ABC):
|
||||
"""插件类型"""
|
||||
license: str = "MIT License"
|
||||
"""插件发布时采用的许可协议"""
|
||||
dependencies: Sequence[str] = []
|
||||
dependencies: Sequence[str] = tuple()
|
||||
"""插件是否对其他插件存在依赖"""
|
||||
|
||||
|
||||
class TopBasePlugin(ABC):
|
||||
class TopPluginBase(ABC):
|
||||
"""所有插件的抽象基类"""
|
||||
|
||||
metainfo: PluginMetaInformation
|
||||
@@ -180,14 +183,15 @@ class TopBasePlugin(ABC):
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise PluginMetainfoNotFoundError(
|
||||
"类`{cls_name}`必须定义一个`metainfo`属性。".format(
|
||||
cls_name=cls.__name__
|
||||
if not cls.__name__.endswith("PluginBase"):
|
||||
raise PluginMetainfoNotFoundError(
|
||||
"类`{cls_name}`必须定义一个`metainfo`属性。".format(
|
||||
cls_name=cls.__name__
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TopInOutBasePlugin(TopBasePlugin, ABC):
|
||||
class TopInOutPluginBase(TopPluginBase, ABC):
|
||||
"""导入导出用抽象基类"""
|
||||
|
||||
supported_formats: Tuple[str, ...] = tuple()
|
||||
@@ -218,15 +222,15 @@ class TopInOutBasePlugin(TopBasePlugin, ABC):
|
||||
return format_name.upper().endswith(self.supported_formats)
|
||||
|
||||
|
||||
class MusicInputPlugin(TopInOutBasePlugin, ABC):
|
||||
class MusicInputPluginBase(TopInOutPluginBase, ABC):
|
||||
"""导入用插件抽象基类-完整曲目"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_IMPORT:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_IMPORT:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`MusicInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`MusicInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -245,15 +249,15 @@ class MusicInputPlugin(TopInOutBasePlugin, ABC):
|
||||
return self.loadbytes(f, config)
|
||||
|
||||
|
||||
class TrackInputPlugin(TopInOutBasePlugin, ABC):
|
||||
class TrackInputPluginBase(TopInOutPluginBase, ABC):
|
||||
"""导入用插件抽象基类-单个音轨"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_IMPORT:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_IMPORT:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`TrackInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`TrackInputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_IMPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -272,15 +276,15 @@ class TrackInputPlugin(TopInOutBasePlugin, ABC):
|
||||
return self.loadbytes(f, config)
|
||||
|
||||
|
||||
class MusicOperatePlugin(TopBasePlugin, ABC):
|
||||
class MusicOperatePluginBase(TopPluginBase, ABC):
|
||||
"""音乐处理用插件抽象基类-完整曲目"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_OPERATE:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_OPERATE:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`MusicOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`MusicOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -294,15 +298,15 @@ class MusicOperatePlugin(TopBasePlugin, ABC):
|
||||
pass
|
||||
|
||||
|
||||
class TrackOperatePlugin(TopBasePlugin, ABC):
|
||||
class TrackOperatePluginBase(TopPluginBase, ABC):
|
||||
"""音乐处理用插件抽象基类-单个音轨"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_OPERATE:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_OPERATE:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`TrackOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`TrackOperatePlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_OPERATE`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -316,15 +320,15 @@ class TrackOperatePlugin(TopBasePlugin, ABC):
|
||||
pass
|
||||
|
||||
|
||||
class MusicOutputPlugin(TopInOutBasePlugin, ABC):
|
||||
class MusicOutputPluginBase(TopInOutPluginBase, ABC):
|
||||
"""导出用插件的抽象基类-完整曲目"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_EXPORT:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_MUSIC_EXPORT:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`MusicOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`MusicOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_MUSIC_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -345,15 +349,15 @@ class MusicOutputPlugin(TopInOutBasePlugin, ABC):
|
||||
pass
|
||||
|
||||
|
||||
class TrackOutputPlugin(TopInOutBasePlugin, ABC):
|
||||
class TrackOutputPluginBase(TopInOutPluginBase, ABC):
|
||||
"""导出用插件的抽象基类-单个音轨"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
super().__init_subclass__()
|
||||
|
||||
if cls.metainfo.type != PluginType.FUNCTION_EXPORT:
|
||||
if cls.metainfo.type != PluginType.FUNCTION_TRACK_EXPORT:
|
||||
raise PluginMetainfoValueError(
|
||||
"插件类`{cls_name}`是从`TrackOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
"插件类`{cls_name}`是从`TrackOutputPlugin`继承的,该类的子类应当为一个`PluginType.FUNCTION_TRACK_EXPORT`类型的插件,而不是`PluginType.{cls_type}`".format(
|
||||
cls_name=cls.__name__,
|
||||
cls_type=cls.metainfo.type.name,
|
||||
)
|
||||
@@ -374,7 +378,7 @@ class TrackOutputPlugin(TopInOutBasePlugin, ABC):
|
||||
pass
|
||||
|
||||
|
||||
class ServicePlugin(TopBasePlugin, ABC):
|
||||
class ServicePluginBase(TopPluginBase, ABC):
|
||||
"""服务插件抽象基类"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
@@ -394,7 +398,7 @@ class ServicePlugin(TopBasePlugin, ABC):
|
||||
pass
|
||||
|
||||
|
||||
class LibraryPlugin(TopBasePlugin, ABC):
|
||||
class LibraryPluginBase(TopPluginBase, ABC):
|
||||
"""插件依赖库的抽象基类"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
|
||||
59
Musicreater/builtin_plugins/midi_read.py
Normal file
59
Musicreater/builtin_plugins/midi_read.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
音·创 v3 内置的 Midi 读取插件
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2026 金羿、玉衡Alioth
|
||||
Copyright © 2026 Eilles, YuhengAlioth
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
"""
|
||||
|
||||
# 睿乐组织 开发交流群 861684859
|
||||
# Email TriM-Organization@hotmail.com
|
||||
# 若需转载或借鉴 许可声明请查看仓库目录下的 License.md
|
||||
|
||||
import mido
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from typing import BinaryIO, Optional
|
||||
|
||||
from Musicreater import SingleMusic
|
||||
from Musicreater.plugins import (
|
||||
music_input_plugin,
|
||||
PluginConfig,
|
||||
PluginMetaInformation,
|
||||
PluginType,
|
||||
MusicInputPluginBase,
|
||||
)
|
||||
|
||||
|
||||
@music_input_plugin("midi_2_music_plugin")
|
||||
class MidiImport2MusicPlugin(MusicInputPluginBase):
|
||||
"""Midi 音乐数据导入插件"""
|
||||
|
||||
metainfo = PluginMetaInformation(
|
||||
name="midi_2_music_plugin",
|
||||
author="金羿、玉衡Alioth",
|
||||
description="从 Midi 文件导入音乐数据",
|
||||
version=(0, 0, 1),
|
||||
type=PluginType.FUNCTION_MUSIC_IMPORT,
|
||||
license="Same as Musicreater",
|
||||
)
|
||||
|
||||
supported_formats = ("MID", "MIDI")
|
||||
|
||||
def loadbytes(
|
||||
self, bytes_buffer_in: BinaryIO, config: PluginConfig | None
|
||||
) -> SingleMusic:
|
||||
midi_file = mido.MidiFile(file=bytes_buffer_in)
|
||||
return SingleMusic() # =========================== TODO: 等待制作
|
||||
|
||||
def load(self, file_path: Path, config: Optional[PluginConfig]) -> "SingleMusic":
|
||||
"""从 Midi 文件导入音乐数据"""
|
||||
midi_file = mido.MidiFile(filename=file_path)
|
||||
return SingleMusic() # =========================== TODO: 等待制作
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2025 Eilles & bgArray
|
||||
版权所有 © 2026 金羿 & 诸葛亮与八卦阵
|
||||
Copyright © 2026 Eilles & bgArray
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -44,9 +44,6 @@ MIDI_PAN = "pan"
|
||||
"""Midi通道立体声场偏移"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Midi用对照表
|
||||
|
||||
MIDI_DEFAULT_VOLUME_VALUE: int = (
|
||||
|
||||
@@ -421,7 +421,7 @@ class SingleTrack(List[SingleNote]):
|
||||
track_name: str
|
||||
"""轨道之名称"""
|
||||
|
||||
is_enabled: bool
|
||||
is_enabled: bool = True
|
||||
"""该音轨是否启用"""
|
||||
|
||||
track_instrument: str
|
||||
@@ -453,16 +453,12 @@ class SingleTrack(List[SingleNote]):
|
||||
precise_time: bool = True,
|
||||
percussion: bool = False,
|
||||
sound_direction: SoundAtmos = SoundAtmos(),
|
||||
enabled: bool = True,
|
||||
extra_information: Dict[str, Any] = {},
|
||||
*args: SingleNote,
|
||||
):
|
||||
self.track_name = name
|
||||
"""音轨名称"""
|
||||
|
||||
self.is_enabled = enabled
|
||||
"""音轨启用情况"""
|
||||
|
||||
self.track_instrument = instrument
|
||||
"""乐器ID"""
|
||||
|
||||
@@ -492,6 +488,21 @@ class SingleTrack(List[SingleNote]):
|
||||
super().__init__(*args)
|
||||
super().sort()
|
||||
|
||||
def disable(self) -> None:
|
||||
"""禁用音轨"""
|
||||
|
||||
self.is_enabled = False
|
||||
|
||||
def enable(self) -> None:
|
||||
"""启用音轨"""
|
||||
|
||||
self.is_enabled = True
|
||||
|
||||
def toggle_able(self) -> None:
|
||||
"""切换音轨的启用状态"""
|
||||
|
||||
self.is_enabled = not self.is_enabled
|
||||
|
||||
def append(self, object: SingleNote) -> None:
|
||||
"""
|
||||
添加一个音符,推荐使用 add 方法
|
||||
@@ -728,13 +739,15 @@ class SingleMusic(List[SingleTrack]):
|
||||
self, start_time: float, end_time: float = inf
|
||||
) -> Generator[Iterator[SingleNote], Any, None]:
|
||||
"""获取指定时间段的各个音轨的音符数据"""
|
||||
return (track.get_notes(start_time, end_time) for track in self)
|
||||
return (track.get_notes(start_time, end_time) for track in self.music_tracks)
|
||||
|
||||
def get_tracked_minenotes(
|
||||
self, start_time: float, end_time: float = inf
|
||||
) -> Generator[Iterator[MineNote], Any, None]:
|
||||
"""获取指定时间段的各个音轨的,供我的世界播放的音符数据类"""
|
||||
return (track.get_minenotes(start_time, end_time) for track in self)
|
||||
return (
|
||||
track.get_minenotes(start_time, end_time) for track in self.music_tracks
|
||||
)
|
||||
|
||||
def get_notes(
|
||||
self, start_time: float, end_time: float = inf
|
||||
@@ -743,7 +756,7 @@ class SingleMusic(List[SingleTrack]):
|
||||
if self.track_amount == 0:
|
||||
return iter(())
|
||||
return self.yield_from_tracks(
|
||||
[track.get_notes(start_time, end_time) for track in self],
|
||||
[track.get_notes(start_time, end_time) for track in self.music_tracks],
|
||||
sort_key=lambda x: x.start_time,
|
||||
)
|
||||
|
||||
@@ -754,7 +767,7 @@ class SingleMusic(List[SingleTrack]):
|
||||
if self.track_amount == 0:
|
||||
return
|
||||
yield from self.yield_from_tracks(
|
||||
[track.get_minenotes(start_time, end_time) for track in self],
|
||||
[track.get_minenotes(start_time, end_time) for track in self.music_tracks],
|
||||
sort_key=lambda x: x.start_tick,
|
||||
)
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿 & 玉衡Alioth
|
||||
Copyright © 2025 Eilles & YuhengAlioth
|
||||
版权所有 © 2026 金羿 & 玉衡Alioth
|
||||
Copyright © 2026 Eilles & YuhengAlioth
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -56,6 +56,7 @@ class MusicreaterInnerlyError(MusicreaterBaseException):
|
||||
"""内部错误(面向开发者的报错信息)"""
|
||||
super().__init__("内部错误 - ", *args)
|
||||
|
||||
|
||||
class MusicreaterOuterlyError(MusicreaterBaseException):
|
||||
"""外部错误"""
|
||||
|
||||
@@ -64,8 +65,6 @@ class MusicreaterOuterlyError(MusicreaterBaseException):
|
||||
super().__init__("外部错误 - ", *args)
|
||||
|
||||
|
||||
|
||||
|
||||
class InnerlyParameterError(MusicreaterInnerlyError):
|
||||
"""内部传参错误"""
|
||||
|
||||
@@ -90,6 +89,13 @@ class ParameterValueError(InnerlyParameterError, ValueError):
|
||||
super().__init__("参数数值错误:", *args)
|
||||
|
||||
|
||||
class PluginNotSpecifiedError(InnerlyParameterError, LookupError):
|
||||
"""未指定插件"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""未指定插件"""
|
||||
super().__init__("未指定插件:", *args)
|
||||
|
||||
|
||||
class OuterlyParameterError(MusicreaterOuterlyError):
|
||||
"""外部参数错误"""
|
||||
@@ -115,6 +121,12 @@ class IllegalMinimumVolumeError(OuterlyParameterError, ValueError):
|
||||
super().__init__("最小播放音量超出范围:", *args)
|
||||
|
||||
|
||||
class FileFormatNotSupportedError(MusicreaterOuterlyError):
|
||||
"""不支持的文件格式"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""文件格式不受支持"""
|
||||
super().__init__("不支持的文件格式:", *args)
|
||||
|
||||
|
||||
class NoteBinaryDecodeError(MusicreaterOuterlyError):
|
||||
@@ -148,6 +160,7 @@ class NoteBinaryFileVerificationFailed(NoteBinaryDecodeError):
|
||||
"""音乐存储文件与其校验值不一致"""
|
||||
super().__init__("音乐存储文件校验失败:", *args)
|
||||
|
||||
|
||||
class PluginDefineError(MusicreaterInnerlyError):
|
||||
"""插件定义错误(内部相关)"""
|
||||
|
||||
@@ -155,6 +168,7 @@ class PluginDefineError(MusicreaterInnerlyError):
|
||||
"""插件本身存在错误"""
|
||||
super().__init__("插件内部错误 - ", *args)
|
||||
|
||||
|
||||
class PluginInstanceNotFoundError(PluginDefineError, LookupError):
|
||||
"""插件实例未找到"""
|
||||
|
||||
@@ -162,6 +176,7 @@ class PluginInstanceNotFoundError(PluginDefineError, LookupError):
|
||||
"""插件实例未找到"""
|
||||
super().__init__("插件实例未找到:", *args)
|
||||
|
||||
|
||||
class PluginAttributeNotFoundError(PluginDefineError, AttributeError):
|
||||
"""插件属性定义错误"""
|
||||
|
||||
@@ -177,6 +192,7 @@ class PluginMetainfoError(PluginDefineError):
|
||||
"""插件元信息定义错误"""
|
||||
super().__init__("插件元信息定义错误 - ", *args)
|
||||
|
||||
|
||||
class PluginMetainfoTypeError(PluginMetainfoError, TypeError):
|
||||
"""插件元信息定义类型错误"""
|
||||
|
||||
@@ -184,6 +200,7 @@ class PluginMetainfoTypeError(PluginMetainfoError, TypeError):
|
||||
"""插件元信息定义类型错误"""
|
||||
super().__init__("插件元信息类型错误:", *args)
|
||||
|
||||
|
||||
class PluginMetainfoValueError(PluginMetainfoError, ValueError):
|
||||
"""插件元信息定义值错误"""
|
||||
|
||||
@@ -191,6 +208,7 @@ class PluginMetainfoValueError(PluginMetainfoError, ValueError):
|
||||
"""插件元信息定义值错误"""
|
||||
super().__init__("插件元信息数值错误:", *args)
|
||||
|
||||
|
||||
class PluginMetainfoNotFoundError(PluginMetainfoError, PluginAttributeNotFoundError):
|
||||
"""插件元信息定义缺少错误"""
|
||||
|
||||
|
||||
@@ -43,13 +43,24 @@ https://gitee.com/TriM-Organization/Musicreater/blob/master/LICENSE.md。
|
||||
# Words combine! Codes unite!
|
||||
# Hurry to call the programmer! Let's Go!
|
||||
|
||||
import re
|
||||
|
||||
|
||||
from difflib import get_close_matches
|
||||
from typing import Dict, Generator, List, Optional, Tuple, Union
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
from .data import SingleMusic, SingleTrack
|
||||
from ._plugin_abc import TopBasePlugin
|
||||
from .plugins import __global_plugin_registry, PluginRegistry
|
||||
from .exceptions import FileFormatNotSupportedError, PluginNotSpecifiedError
|
||||
from ._plugin_abc import TopPluginBase
|
||||
from .plugins import (
|
||||
_global_plugin_registry,
|
||||
PluginRegistry,
|
||||
PluginConfig,
|
||||
PluginType,
|
||||
load_plugin_module,
|
||||
)
|
||||
|
||||
|
||||
class MusiCreater:
|
||||
@@ -60,21 +71,239 @@ class MusiCreater:
|
||||
|
||||
__plugin_registry: PluginRegistry
|
||||
"""插件注册表实例"""
|
||||
_plugin_cache: Dict[str, TopBasePlugin]
|
||||
_plugin_cache: Dict[str, TopPluginBase]
|
||||
"""插件缓存字典,插件名为键、插件实例为值"""
|
||||
music: SingleMusic
|
||||
"""当前曲目实例"""
|
||||
|
||||
def __init__(self, whole_music: SingleMusic) -> None:
|
||||
global __global_plugin_registry
|
||||
global _global_plugin_registry
|
||||
|
||||
self.__plugin_registry = __global_plugin_registry
|
||||
self.__plugin_registry = _global_plugin_registry
|
||||
|
||||
self._plugin_cache = {}
|
||||
self._cache_all_plugins()
|
||||
|
||||
self.music = whole_music
|
||||
|
||||
|
||||
|
||||
|
||||
def import_music(self, file_path: Path, plugin_name: Optional[str] = None) -> SingleMusic:
|
||||
@classmethod
|
||||
def import_music(
|
||||
cls,
|
||||
file_path: Path,
|
||||
plugin_name: Optional[str] = None,
|
||||
plugin_config: Optional[PluginConfig] = None,
|
||||
):
|
||||
__music = None
|
||||
if plugin_name:
|
||||
__music = _global_plugin_registry.get_music_input_plugin(plugin_name).load(
|
||||
file_path, plugin_config
|
||||
)
|
||||
else:
|
||||
for plugin in _global_plugin_registry.get_music_input_plugin_by_format(
|
||||
file_path
|
||||
):
|
||||
if __music is not None:
|
||||
raise PluginNotSpecifiedError(
|
||||
"文件类型`{}`可被多个插件处理,请在导入函数的参数中指定插件名称".format(
|
||||
file_path.suffix.upper()
|
||||
)
|
||||
)
|
||||
__music = plugin.load(file_path, plugin_config)
|
||||
if __music is None:
|
||||
raise FileFormatNotSupportedError(
|
||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
||||
)
|
||||
return cls(whole_music=__music)
|
||||
|
||||
def import_track(
|
||||
self,
|
||||
file_path: Path,
|
||||
plugin_name: Optional[str] = None,
|
||||
plugin_config: Optional[PluginConfig] = None,
|
||||
) -> SingleTrack:
|
||||
__track = None
|
||||
if plugin_name:
|
||||
__track = self.get_plugin_by_name(
|
||||
plugin_name
|
||||
).load( # pyright: ignore[reportAttributeAccessIssue]
|
||||
file_path, plugin_config
|
||||
)
|
||||
else:
|
||||
for plugin in self.__plugin_registry.get_track_input_plugin_by_format(
|
||||
file_path
|
||||
):
|
||||
if __track:
|
||||
raise PluginNotSpecifiedError(
|
||||
"文件类型`{}`可被多个插件处理,请在导入函数的参数中指定插件名称".format(
|
||||
file_path.suffix.upper()
|
||||
)
|
||||
)
|
||||
__track = plugin.load(file_path, plugin_config)
|
||||
if __track:
|
||||
self.music.append(__track)
|
||||
return __track
|
||||
raise FileFormatNotSupportedError(
|
||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
||||
)
|
||||
|
||||
def export_music(
|
||||
self,
|
||||
file_path: Path,
|
||||
plugin_name: Optional[str] = None,
|
||||
plugin_config: Optional[PluginConfig] = None,
|
||||
) -> None:
|
||||
__plugin = None
|
||||
if plugin_name:
|
||||
__plugin = self.get_plugin_by_name(plugin_name)
|
||||
else:
|
||||
for plugin in self.__plugin_registry.get_music_output_plugin_by_format(
|
||||
file_path
|
||||
):
|
||||
if __plugin:
|
||||
raise PluginNotSpecifiedError(
|
||||
"文件类型`{}`可被多个插件处理,请在导出函数的参数中指定插件名称".format(
|
||||
file_path.suffix.upper()
|
||||
)
|
||||
)
|
||||
__plugin = plugin
|
||||
|
||||
if __plugin:
|
||||
__plugin.dump( # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.music, file_path, plugin_config
|
||||
)
|
||||
else:
|
||||
raise FileFormatNotSupportedError(
|
||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
||||
)
|
||||
|
||||
def export_track(
|
||||
self,
|
||||
track_index: int,
|
||||
file_path: Path,
|
||||
plugin_name: Optional[str] = None,
|
||||
plugin_config: Optional[PluginConfig] = None,
|
||||
) -> None:
|
||||
__plugin = None
|
||||
if plugin_name:
|
||||
__plugin = self.get_plugin_by_name(plugin_name)
|
||||
else:
|
||||
for plugin in self.__plugin_registry.get_track_output_plugin_by_format(
|
||||
file_path
|
||||
):
|
||||
if __plugin:
|
||||
raise PluginNotSpecifiedError(
|
||||
"文件类型`{}`可被多个插件处理,请在导出函数的参数中指定插件名称".format(
|
||||
file_path.suffix.upper()
|
||||
)
|
||||
)
|
||||
__plugin = plugin
|
||||
|
||||
if __plugin:
|
||||
__plugin.dump( # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.music[track_index], file_path, plugin_config
|
||||
)
|
||||
else:
|
||||
raise FileFormatNotSupportedError(
|
||||
"无法找到处理`{}`类型文件的插件".format(file_path.suffix.upper())
|
||||
)
|
||||
|
||||
def perform_operation_on_music(
|
||||
self, plugin_name: str, plugin_config: Optional[PluginConfig] = None
|
||||
):
|
||||
# 这样做是为了兼容以后的*撤回/重做*功能
|
||||
self.music = self.get_plugin_by_name(
|
||||
plugin_name
|
||||
).process( # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.music, plugin_config
|
||||
)
|
||||
|
||||
def perform_operation_on_track(
|
||||
self,
|
||||
track_index: int,
|
||||
plugin_name: str,
|
||||
plugin_config: Optional[PluginConfig] = None,
|
||||
):
|
||||
# 这样做是为了兼容以后的*撤回/重做*功能
|
||||
self.music[track_index] = self.get_plugin_by_name(
|
||||
plugin_name
|
||||
).process( # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.music[track_index], plugin_config
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _camel_to_snake(name: str) -> str:
|
||||
"""
|
||||
将驼峰命名转换为蛇形命名
|
||||
CyberAngel -> cyber_angel
|
||||
"""
|
||||
return re.sub(
|
||||
"([a-z0-9])([A-Z])", r"\1_\2", re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
|
||||
).lower()
|
||||
|
||||
def _parse_plugin_name(self, attr_name: str) -> Optional[str]:
|
||||
"""解析属性名称为插件名称"""
|
||||
|
||||
# 尝试去除 _plugin 后缀
|
||||
if attr_name.endswith("_plugin"):
|
||||
candidate_name = attr_name[:-7] # 去除 "_plugin"
|
||||
if candidate_name in self._plugin_cache:
|
||||
return candidate_name
|
||||
|
||||
# 尝试转换为 snake_case(如果插件名是驼峰式)
|
||||
snake_case_name = self._camel_to_snake(attr_name)
|
||||
|
||||
if snake_case_name != attr_name: # 避免重复转换
|
||||
if snake_case_name in self._plugin_cache: # 尝试转换后的插件名
|
||||
return snake_case_name
|
||||
else:
|
||||
return self._parse_plugin_name(snake_case_name)
|
||||
|
||||
return None
|
||||
|
||||
def _get_closest_plugin_name(self, requested_name: str) -> Optional[str]:
|
||||
"""找到最接近的插件名称(用于更好的错误提示)"""
|
||||
|
||||
matches = get_close_matches(
|
||||
requested_name, self._plugin_cache.keys(), n=1, cutoff=0.6
|
||||
)
|
||||
return matches[0] if matches else None
|
||||
|
||||
def get_plugin_by_name(self, name: str) -> TopPluginBase:
|
||||
"""获取插件实例,并缓存起来,提高性能"""
|
||||
if name.startswith("_"):
|
||||
raise AttributeError("属性`{}`不存在,不应访问类的私有属性".format(name))
|
||||
|
||||
if name in self._plugin_cache:
|
||||
return self._plugin_cache[name]
|
||||
else:
|
||||
plugin_name = self._parse_plugin_name(name)
|
||||
if plugin_name:
|
||||
self._plugin_cache[name] = self._plugin_cache[plugin_name]
|
||||
return self._plugin_cache[name]
|
||||
|
||||
closest = self._get_closest_plugin_name(name)
|
||||
|
||||
raise AttributeError(
|
||||
"插件`{}`不存在,请检查插件名称是否正确".format(name)
|
||||
+ (
|
||||
";或者阁下可能想要使用的是`{}`插件?".format(closest)
|
||||
if closest
|
||||
else ""
|
||||
)
|
||||
)
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
"""动态属性访问,允许直接 实例.插件名 来访问插件"""
|
||||
return self.get_plugin_by_name(name)
|
||||
|
||||
def _cache_all_plugins(self):
|
||||
"""获取所有已注册插件的名称"""
|
||||
for __plugin_type, __plugins_set in self.__plugin_registry:
|
||||
for __plugin in __plugins_set:
|
||||
if __plugin.metainfo.name in self._plugin_cache: # 避免重复缓存
|
||||
if (
|
||||
__plugin.metainfo.version
|
||||
<= self._plugin_cache[__plugin.metainfo.name].metainfo.version
|
||||
): # 优先使用版本号最大的插件
|
||||
continue
|
||||
self._plugin_cache[__plugin.metainfo.name] = __plugin
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿
|
||||
Copyright © 2025 Eilles
|
||||
版权所有 © 2026 金羿
|
||||
Copyright © 2026 Eilles
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -19,7 +19,18 @@ Terms & Conditions: License.md in the root directory
|
||||
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional, List, Tuple, Union, Generator, Set
|
||||
from typing import (
|
||||
Dict,
|
||||
Any,
|
||||
Optional,
|
||||
List,
|
||||
Tuple,
|
||||
Union,
|
||||
Generator,
|
||||
Set,
|
||||
Iterable,
|
||||
Iterator,
|
||||
)
|
||||
from itertools import chain
|
||||
|
||||
|
||||
@@ -30,14 +41,16 @@ from ._plugin_abc import (
|
||||
PluginConfig,
|
||||
PluginMetaInformation,
|
||||
# 抽象基类(插件定义)
|
||||
MusicInputPlugin,
|
||||
TrackInputPlugin,
|
||||
MusicOperatePlugin,
|
||||
TrackOperatePlugin,
|
||||
MusicOutputPlugin,
|
||||
TrackOutputPlugin,
|
||||
ServicePlugin,
|
||||
LibraryPlugin,
|
||||
MusicInputPluginBase,
|
||||
TrackInputPluginBase,
|
||||
MusicOperatePluginBase,
|
||||
TrackOperatePluginBase,
|
||||
MusicOutputPluginBase,
|
||||
TrackOutputPluginBase,
|
||||
ServicePluginBase,
|
||||
LibraryPluginBase,
|
||||
# 顶层插件定义
|
||||
TopPluginBase,
|
||||
)
|
||||
from .exceptions import (
|
||||
PluginMetainfoNotFoundError,
|
||||
@@ -53,14 +66,14 @@ __all__ = [
|
||||
"PluginConfig",
|
||||
"PluginMetaInformation",
|
||||
# 抽象基类(插件定义)
|
||||
"MusicInputPlugin",
|
||||
"TrackInputPlugin",
|
||||
"MusicOperatePlugin",
|
||||
"TrackOperatePlugin",
|
||||
"MusicOutputPlugin",
|
||||
"TrackOutputPlugin",
|
||||
"ServicePlugin",
|
||||
"LibraryPlugin",
|
||||
"MusicInputPluginBase",
|
||||
"TrackInputPluginBase",
|
||||
"MusicOperatePluginBase",
|
||||
"TrackOperatePluginBase",
|
||||
"MusicOutputPluginBase",
|
||||
"TrackOutputPluginBase",
|
||||
"ServicePluginBase",
|
||||
"LibraryPluginBase",
|
||||
# 插件注册用装饰函数
|
||||
"music_input_plugin",
|
||||
"track_input_plugin",
|
||||
@@ -102,14 +115,34 @@ class PluginRegistry:
|
||||
# (不用 Dict[str`plugin name`, PluginClass`] 的形式)
|
||||
# 啊,我真的很高尚
|
||||
# 你真的不会把插件注册两遍吧……对吧?
|
||||
self._music_input_plugins: Set[MusicInputPlugin] = set()
|
||||
self._track_input_plugins: Set[TrackInputPlugin] = set()
|
||||
self._music_operate_plugins: Set[MusicOperatePlugin] = set()
|
||||
self._track_operate_plugins: Set[TrackOperatePlugin] = set()
|
||||
self._music_output_plugins: Set[MusicOutputPlugin] = set()
|
||||
self._track_output_plugins: Set[TrackOutputPlugin] = set()
|
||||
self._service_plugins: Set[ServicePlugin] = set()
|
||||
self._library_plugins: Set[LibraryPlugin] = set()
|
||||
|
||||
# EMERGENCY TODO ================================================ CRITICAL 紧急更改
|
||||
# 改成 Dict[str`plugin id`, PluginClass`]] 的形式吧
|
||||
# 刚刚才想起来,这个 name 是显示名称啊!草
|
||||
# 现在测试情况下就将错就错吧,先把 name 当成 id 来写吧
|
||||
self._music_input_plugins: Set[MusicInputPluginBase] = set()
|
||||
self._track_input_plugins: Set[TrackInputPluginBase] = set()
|
||||
self._music_operate_plugins: Set[MusicOperatePluginBase] = set()
|
||||
self._track_operate_plugins: Set[TrackOperatePluginBase] = set()
|
||||
self._music_output_plugins: Set[MusicOutputPluginBase] = set()
|
||||
self._track_output_plugins: Set[TrackOutputPluginBase] = set()
|
||||
self._service_plugins: Set[ServicePluginBase] = set()
|
||||
self._library_plugins: Set[LibraryPluginBase] = set()
|
||||
|
||||
def __iter__(self) -> Iterator[Tuple[PluginType, Set[TopPluginBase]]]:
|
||||
"""迭代器,返回所有插件"""
|
||||
return iter(
|
||||
(
|
||||
(PluginType.FUNCTION_MUSIC_IMPORT, self._music_input_plugins),
|
||||
(PluginType.FUNCTION_TRACK_IMPORT, self._track_input_plugins),
|
||||
(PluginType.FUNCTION_MUSIC_OPERATE, self._music_operate_plugins),
|
||||
(PluginType.FUNCTION_TRACK_OPERATE, self._track_operate_plugins),
|
||||
(PluginType.FUNCTION_MUSIC_EXPORT, self._music_output_plugins),
|
||||
(PluginType.FUNCTION_TRACK_EXPORT, self._track_output_plugins),
|
||||
(PluginType.SERVICE, self._service_plugins),
|
||||
(PluginType.LIBRARY, self._library_plugins),
|
||||
)
|
||||
) # pyright: ignore[reportReturnType]
|
||||
|
||||
def register_music_input_plugin(self, plugin_class: type) -> None:
|
||||
"""注册输入插件-整首曲目"""
|
||||
@@ -145,7 +178,7 @@ class PluginRegistry:
|
||||
|
||||
def get_music_input_plugin_by_format(
|
||||
self, filepath_or_format: Union[Path, str]
|
||||
) -> Generator[MusicInputPlugin, None, None]:
|
||||
) -> Generator[MusicInputPluginBase, None, None]:
|
||||
"""通过指定输入的文件或格式,以获取对应的全曲导入用插件"""
|
||||
if isinstance(filepath_or_format, str):
|
||||
return (
|
||||
@@ -168,7 +201,7 @@ class PluginRegistry:
|
||||
|
||||
def get_track_input_plugin_by_format(
|
||||
self, filepath_or_format: Union[Path, str]
|
||||
) -> Generator[TrackInputPlugin, None, None]:
|
||||
) -> Generator[TrackInputPluginBase, None, None]:
|
||||
"""通过指定输入的文件或格式,以获取对应的单音轨导入用插件"""
|
||||
if isinstance(filepath_or_format, str):
|
||||
return (
|
||||
@@ -191,7 +224,7 @@ class PluginRegistry:
|
||||
|
||||
def get_music_output_plugin_by_format(
|
||||
self, filepath_or_format: Union[Path, str]
|
||||
) -> Generator[MusicOutputPlugin, None, None]:
|
||||
) -> Generator[MusicOutputPluginBase, None, None]:
|
||||
"""通过指定输出的文件或格式,以获取对应的导出全曲用插件"""
|
||||
if isinstance(filepath_or_format, str):
|
||||
return (
|
||||
@@ -214,7 +247,7 @@ class PluginRegistry:
|
||||
|
||||
def get_track_output_plugin_by_format(
|
||||
self, filepath_or_format: Union[Path, str]
|
||||
) -> Generator[TrackOutputPlugin, None, None]:
|
||||
) -> Generator[TrackOutputPluginBase, None, None]:
|
||||
"""通过指定输出的文件或格式,以获取对应的导出单个音轨用插件"""
|
||||
if isinstance(filepath_or_format, str):
|
||||
return (
|
||||
@@ -235,7 +268,7 @@ class PluginRegistry:
|
||||
)
|
||||
)
|
||||
|
||||
def get_music_input_plugin(self, plugin_name: str) -> MusicInputPlugin:
|
||||
def get_music_input_plugin(self, plugin_name: str) -> MusicInputPluginBase:
|
||||
"""获取指定名称的全曲导入用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -251,7 +284,7 @@ class PluginRegistry:
|
||||
"未找到“用于导入曲目、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_track_input_plugin(self, plugin_name: str) -> TrackInputPlugin:
|
||||
def get_track_input_plugin(self, plugin_name: str) -> TrackInputPluginBase:
|
||||
"""获取指定名称的单音轨导入用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -267,7 +300,7 @@ class PluginRegistry:
|
||||
"未找到“用于导入单个音轨、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_music_operate_plugin(self, plugin_name: str) -> MusicOperatePlugin:
|
||||
def get_music_operate_plugin(self, plugin_name: str) -> MusicOperatePluginBase:
|
||||
"""获取指定名称的全曲处理用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -283,7 +316,7 @@ class PluginRegistry:
|
||||
"未找到“用于处理整个曲目、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_track_operate_plugin(self, plugin_name: str) -> TrackOperatePlugin:
|
||||
def get_track_operate_plugin(self, plugin_name: str) -> TrackOperatePluginBase:
|
||||
"""获取指定名称的单音轨处理用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -299,7 +332,7 @@ class PluginRegistry:
|
||||
"未找到“用于处理单个音轨、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_music_output_plugin(self, plugin_name: str) -> MusicOutputPlugin:
|
||||
def get_music_output_plugin(self, plugin_name: str) -> MusicOutputPluginBase:
|
||||
"""获取指定名称的导出全曲用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -315,7 +348,7 @@ class PluginRegistry:
|
||||
"未找到“用于导出完整曲目、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_track_output_plugin(self, plugin_name: str) -> TrackOutputPlugin:
|
||||
def get_track_output_plugin(self, plugin_name: str) -> TrackOutputPluginBase:
|
||||
"""获取指定名称的导出单音轨用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -331,7 +364,7 @@ class PluginRegistry:
|
||||
"未找到“用于导出单个音轨、名为`{}`”的插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_service_plugin(self, plugin_name: str) -> ServicePlugin:
|
||||
def get_service_plugin(self, plugin_name: str) -> ServicePluginBase:
|
||||
"""获取服务用插件,当名称重叠时,取版本号最大的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -347,7 +380,7 @@ class PluginRegistry:
|
||||
"未找到名为`{}`的服务用插件".format(plugin_name)
|
||||
)
|
||||
|
||||
def get_library_plugin(self, plugin_name: str) -> LibraryPlugin:
|
||||
def get_library_plugin(self, plugin_name: str) -> LibraryPluginBase:
|
||||
"""获取依赖库类插件,当名称重叠时,取版本号最高的"""
|
||||
try:
|
||||
return max(
|
||||
@@ -386,101 +419,101 @@ class PluginRegistry:
|
||||
)
|
||||
|
||||
|
||||
__global_plugin_registry = PluginRegistry()
|
||||
_global_plugin_registry = PluginRegistry()
|
||||
"""全局插件注册表实例"""
|
||||
|
||||
|
||||
def music_input_plugin(metainfo: PluginMetaInformation):
|
||||
def music_input_plugin(plugin_id: str):
|
||||
"""全曲输入用插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_music_input_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_music_input_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def track_input_plugin(metainfo: PluginMetaInformation):
|
||||
def track_input_plugin(plugin_id: str):
|
||||
"""单轨输入用插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_track_input_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_track_input_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def music_operate_plugin(metainfo: PluginMetaInformation):
|
||||
def music_operate_plugin(plugin_id: str):
|
||||
"""全曲处理用插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_music_operate_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_music_operate_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def track_operate_plugin(metainfo: PluginMetaInformation):
|
||||
def track_operate_plugin(plugin_id: str):
|
||||
"""音轨处理插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_track_operate_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_track_operate_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def music_output_plugin(metainfo: PluginMetaInformation):
|
||||
def music_output_plugin(plugin_id: str):
|
||||
"""乐曲输出用插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_music_output_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_music_output_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def track_output_plugin(metainfo: PluginMetaInformation):
|
||||
def track_output_plugin(plugin_id: str):
|
||||
"""音轨输出用插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_track_output_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_track_output_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def service_plugin(metainfo: PluginMetaInformation):
|
||||
def service_plugin(plugin_id: str):
|
||||
"""服务插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_service_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_service_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def library_plugin(metainfo: PluginMetaInformation):
|
||||
def library_plugin(plugin_id: str):
|
||||
"""支持库插件装饰器"""
|
||||
|
||||
def decorator(cls):
|
||||
global __global_plugin_registry
|
||||
cls.metainfo = metainfo
|
||||
__global_plugin_registry.register_library_plugin(cls)
|
||||
global _global_plugin_registry
|
||||
cls.id = plugin_id
|
||||
_global_plugin_registry.register_library_plugin(cls)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"""
|
||||
|
||||
"""
|
||||
版权所有 © 2025 金羿 & 玉衡Alioth
|
||||
Copyright © 2025 Eilles & YuhengAlioth
|
||||
版权所有 © 2026 金羿 & 玉衡Alioth
|
||||
Copyright © 2026 Eilles & YuhengAlioth
|
||||
|
||||
开源相关声明请见 仓库根目录下的 License.md
|
||||
Terms & Conditions: License.md in the root directory
|
||||
@@ -22,4 +22,4 @@ from typing import Callable, Dict, List, Literal, Mapping, Tuple, Union
|
||||
FittingFunctionType = Callable[[float], float]
|
||||
"""
|
||||
拟合函数类型
|
||||
"""
|
||||
"""
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
pip install --upgrade -i https://pypi.python.org/simple Musicreater
|
||||
```
|
||||
|
||||
- 克隆仓库并安装(最新版本但**不推荐**)
|
||||
- 克隆仓库并安装(最新内容但**不推荐**)
|
||||
|
||||
```bash
|
||||
git clone https://gitee.com/TriM-Organization/Musicreater.git
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
[简体中文 🇨🇳](README.md) | English🇬🇧
|
||||
|
||||
**Notice that the localizations of documents may NOT be up-to-date.**
|
||||
**Notice that the localizations of documents may probably NOT be up-to-date. The original document is in Chinese.**
|
||||
|
||||
## Introduction🚀
|
||||
|
||||
|
||||
40
TO-DO.md
Normal file
40
TO-DO.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# 任务清单
|
||||
|
||||
## 待办事项
|
||||
- 乐曲文件格式设计
|
||||
目前想到的是:
|
||||
1. 使用 `.MCT` 作为项目文件的后缀,然后考虑一下格式是否和之前的 MusicSequence 兼容,如果兼容的话可以照旧用 `.MSQ`,如不的话,可以试试想一个新的后缀名作为数据文件后缀
|
||||
2. 要求数据文件支持完全流式读入
|
||||
|
||||
- 音轨静音处理
|
||||
当前没有处理
|
||||
|
||||
- 优化音轨的存储方式
|
||||
当前是用列表,且每一次变动元素都要重新排序,这样消耗太大了,需要优化,改用最小堆形式(heapq)
|
||||
|
||||
- 移植 v2 功能到内置插件
|
||||
目前 v2 的功能有很多,都要移植到 v3。
|
||||
1. 导入 Midi 文件到全曲
|
||||
2. 导入 Midi 文件到指定轨道
|
||||
3. 导出到延迟播放器的结构文件(MCSTRUCTURE、BDX)
|
||||
4. 导出到延迟播放器的附加包
|
||||
5. 导出到积分板播放器的以上两种形式
|
||||
6. 导出到中继器播放器的以上两种形式
|
||||
7. 在 WebSocket 播放器中播放
|
||||
8. 导出到支持神羽资源包的以上 7 种形式
|
||||
9. 对于 Midi 歌词的实验性功能
|
||||
10. 对于 Java 版本适配的实验性功能
|
||||
11. 对于听感优化的实验性功能(插值、偏移)
|
||||
|
||||
- 测试参数曲线的功能
|
||||
|
||||
- 支持导出音符盒构成的音乐
|
||||
|
||||
- 支持导出成 schematic 结构
|
||||
|
||||
## 讨论
|
||||
|
||||
1. 是否应该在插件注册表 PluginRegistry 中采用 `Dict[插件名, 插件对象]` 的形式存储插件?
|
||||
当前不采用这种方式是认为可以兼容一些极端场景下用户将一堆同名插件放在一起的情况。但是就算是插件放在一起,我们也可以有选择地读入注册表,比如依照版本号只读取最高版本的插件,并不需要全部存储在插件注册表中。所以其实用字典来存储是有利的?吗?
|
||||
|
||||
2. 服务插件到底该怎么写?总不能留着一个 PluginType.SERVICE 的插件一直空在那里吧……
|
||||
BIN
old-things/Packer/MSCT_MAIN.MPK
Normal file
BIN
old-things/Packer/MSCT_MAIN.MPK
Normal file
Binary file not shown.
BIN
old-things/Packer/MSCT_PLUGIN.MPK
Normal file
BIN
old-things/Packer/MSCT_PLUGIN.MPK
Normal file
Binary file not shown.
1
old-things/Packer/MSCT_PLUGIN_FUNCTION.MPK
Normal file
1
old-things/Packer/MSCT_PLUGIN_FUNCTION.MPK
Normal file
@@ -0,0 +1 @@
|
||||
q@v,fxіБ<D196>Еџ<D095>лцmЩ5]Ќs"ЏџЦбBMXi<58>ЈnНхч<D185>Z8О=Г<7F>4<EFBFBD>PTUБQЈmтфджG<D0B6>жu_цп<D186>DS№|
|
||||
6
old-things/Packer/checksum.txt
Normal file
6
old-things/Packer/checksum.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
MSCT_MAIN:
|
||||
6b9f5a97d50beb07c834e375104c67ae44c57ae40f73fb71075b3668899029c7
|
||||
MSCT_PLUGIN:
|
||||
c280413a394a539438a5d10078c9b55f04bcd4cf6869c59a3f7a026039748cfc
|
||||
MSCT_PLUGIN_FUNCTION:
|
||||
40697f1d9b293268fe142fa3e9bffee2923a8f4811ec7bbdf7b14afb98723ef2
|
||||
0
old-things/bgArrayLib/__init__.py
Normal file
0
old-things/bgArrayLib/__init__.py
Normal file
67
old-things/bgArrayLib/bpm.py
Normal file
67
old-things/bgArrayLib/bpm.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import mido
|
||||
import numpy
|
||||
|
||||
'''
|
||||
bpm
|
||||
bites per minutes
|
||||
每分钟的拍数
|
||||
'''
|
||||
|
||||
def mt2gt(mt, tpb_a, bpm_a):
|
||||
return round(mt / tpb_a / bpm_a * 60)
|
||||
|
||||
|
||||
def get(mid:mido.MidiFile) -> int:
|
||||
'''传入一个 MidiFile, 返回其音乐的bpm
|
||||
:param mid : mido.MidFile
|
||||
mido库识别的midi文件数据
|
||||
:return bpm : int
|
||||
'''
|
||||
# mid = mido.MidiFile(mf)
|
||||
length = mid.length
|
||||
tpb = mid.ticks_per_beat
|
||||
bpm = 20
|
||||
gotV = 0
|
||||
|
||||
for track in mid.tracks:
|
||||
global_time = 0
|
||||
for msg in track:
|
||||
global_time += msg.time
|
||||
if msg.type == "note_on" and msg.velocity > 0:
|
||||
gotV = mt2gt(global_time, tpb, bpm)
|
||||
errorV = numpy.fabs(gotV - length)
|
||||
last_dic = {bpm: errorV}
|
||||
if last_dic.get(bpm) > errorV:
|
||||
last_dic = {bpm: errorV}
|
||||
bpm += 2
|
||||
|
||||
while True:
|
||||
for track in mid.tracks:
|
||||
global_time = 0
|
||||
for msg in track:
|
||||
global_time += msg.time
|
||||
if msg.type == "note_on" and msg.velocity > 0:
|
||||
gotV = mt2gt(global_time, tpb, bpm)
|
||||
errorV = numpy.fabs(gotV - length)
|
||||
try:
|
||||
if last_dic.get(bpm - 2) > errorV:
|
||||
last_dic = {bpm: errorV}
|
||||
except TypeError:
|
||||
pass
|
||||
bpm += 2
|
||||
if bpm >= 252:
|
||||
break
|
||||
print(list(last_dic.keys())[0])
|
||||
return list(last_dic.keys())[0]
|
||||
|
||||
|
||||
def compute(mid:mido.MidiFile):
|
||||
answer = 60000000/mid.ticks_per_beat
|
||||
print(answer)
|
||||
return answer
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mid = mido.MidiFile(r"C:\Users\lc\Documents\MuseScore3\乐谱\乐谱\Bad style - Time back.mid")
|
||||
get(mid)
|
||||
compute(mid)
|
||||
40
old-things/bgArrayLib/compute.py
Normal file
40
old-things/bgArrayLib/compute.py
Normal file
@@ -0,0 +1,40 @@
|
||||
def round_up(num, power=0):
|
||||
"""
|
||||
实现精确四舍五入,包含正、负小数多种场景
|
||||
:param num: 需要四舍五入的小数
|
||||
:param power: 四舍五入位数,支持0-∞
|
||||
:return: 返回四舍五入后的结果
|
||||
"""
|
||||
try:
|
||||
print(1 / 0)
|
||||
except ZeroDivisionError:
|
||||
digit = 10 ** power
|
||||
num2 = float(int(num * digit))
|
||||
# 处理正数,power不为0的情况
|
||||
if num >= 0 and power != 0:
|
||||
tag = num * digit - num2 + 1 / (digit * 10)
|
||||
if tag >= 0.5:
|
||||
return (num2 + 1) / digit
|
||||
else:
|
||||
return num2 / digit
|
||||
# 处理正数,power为0取整的情况
|
||||
elif num >= 0 and power == 0:
|
||||
tag = num * digit - int(num)
|
||||
if tag >= 0.5:
|
||||
return (num2 + 1) / digit
|
||||
else:
|
||||
return num2 / digit
|
||||
# 处理负数,power为0取整的情况
|
||||
elif power == 0 and num < 0:
|
||||
tag = num * digit - int(num)
|
||||
if tag <= -0.5:
|
||||
return (num2 - 1) / digit
|
||||
else:
|
||||
return num2 / digit
|
||||
# 处理负数,power不为0的情况
|
||||
else:
|
||||
tag = num * digit - num2 - 1 / (digit * 10)
|
||||
if tag <= -0.5:
|
||||
return (num2 - 1) / digit
|
||||
else:
|
||||
return num2 / digit
|
||||
130
old-things/bgArrayLib/instrumentConstant.py
Normal file
130
old-things/bgArrayLib/instrumentConstant.py
Normal file
@@ -0,0 +1,130 @@
|
||||
instrument_list = {
|
||||
"0": "harp",
|
||||
"1": "harp",
|
||||
"2": "pling",
|
||||
"3": "harp",
|
||||
"4": "pling",
|
||||
"5": "pling",
|
||||
"6": "harp",
|
||||
"7": "harp",
|
||||
"8": "share",
|
||||
"9": "harp",
|
||||
"10": "didgeridoo",
|
||||
"11": "harp",
|
||||
"12": "xylophone",
|
||||
"13": "chime",
|
||||
"14": "harp",
|
||||
"15": "harp",
|
||||
"16": "bass",
|
||||
"17": "harp",
|
||||
"18": "harp",
|
||||
"19": "harp",
|
||||
"20": "harp",
|
||||
"21": "harp",
|
||||
"22": "harp",
|
||||
"23": "guitar",
|
||||
"24": "guitar",
|
||||
"25": "guitar",
|
||||
"26": "guitar",
|
||||
"27": "guitar",
|
||||
"28": "guitar",
|
||||
"29": "guitar",
|
||||
"30": "guitar",
|
||||
"31": "bass",
|
||||
"32": "bass",
|
||||
"33": "bass",
|
||||
"34": "bass",
|
||||
"35": "bass",
|
||||
"36": "bass",
|
||||
"37": "bass",
|
||||
"38": "bass",
|
||||
"39": "bass",
|
||||
"40": "harp",
|
||||
"41": "harp",
|
||||
"42": "harp",
|
||||
"43": "harp",
|
||||
"44": "iron_xylophone",
|
||||
"45": "guitar",
|
||||
"46": "harp",
|
||||
"47": "harp",
|
||||
"48": "guitar",
|
||||
"49": "guitar",
|
||||
"50": "bit",
|
||||
"51": "bit",
|
||||
"52": "harp",
|
||||
"53": "harp",
|
||||
"54": "bit",
|
||||
"55": "flute",
|
||||
"56": "flute",
|
||||
"57": "flute",
|
||||
"58": "flute",
|
||||
"59": "flute",
|
||||
"60": "flute",
|
||||
"61": "flute",
|
||||
"62": "flute",
|
||||
"63": "flute",
|
||||
"64": "bit",
|
||||
"65": "bit",
|
||||
"66": "bit",
|
||||
"67": "bit",
|
||||
"68": "flute",
|
||||
"69": "harp",
|
||||
"70": "harp",
|
||||
"71": "flute",
|
||||
"72": "flute",
|
||||
"73": "flute",
|
||||
"74": "harp",
|
||||
"75": "flute",
|
||||
"76": "harp",
|
||||
"77": "harp",
|
||||
"78": "harp",
|
||||
"79": "harp",
|
||||
"80": "bit",
|
||||
"81": "bit",
|
||||
"82": "bit",
|
||||
"83": "bit",
|
||||
"84": "bit",
|
||||
"85": "bit",
|
||||
"86": "bit",
|
||||
"87": "bit",
|
||||
"88": "bit",
|
||||
"89": "bit",
|
||||
"90": "bit",
|
||||
"91": "bit",
|
||||
"92": "bit",
|
||||
"93": "bit",
|
||||
"94": "bit",
|
||||
"95": "bit",
|
||||
"96": "bit",
|
||||
"97": "bit",
|
||||
"98": "bit",
|
||||
"99": "bit",
|
||||
"100": "bit",
|
||||
"101": "bit",
|
||||
"102": "bit",
|
||||
"103": "bit",
|
||||
"104": "harp",
|
||||
"105": "banjo",
|
||||
"106": "harp",
|
||||
"107": "harp",
|
||||
"108": "harp",
|
||||
"109": "harp",
|
||||
"110": "harp",
|
||||
"111": "guitar",
|
||||
"112": "harp",
|
||||
"113": "bell",
|
||||
"114": "harp",
|
||||
"115": "cow_bell",
|
||||
"116": "basedrum",
|
||||
"117": "bass",
|
||||
"118": "bit",
|
||||
"119": "basedrum",
|
||||
"120": "guitar",
|
||||
"121": "harp",
|
||||
"122": "harp",
|
||||
"123": "harp",
|
||||
"124": "harp",
|
||||
"125": "hat",
|
||||
"126": "basedrum",
|
||||
"127": "snare",
|
||||
}
|
||||
250
old-things/bgArrayLib/namesConstant.py
Normal file
250
old-things/bgArrayLib/namesConstant.py
Normal file
@@ -0,0 +1,250 @@
|
||||
zip_name = {
|
||||
-1: "-1.Acoustic_Kit_打击乐.zip",
|
||||
0: "0.Acoustic_Grand_Piano_大钢琴.zip",
|
||||
1: "1.Bright_Acoustic_Piano_亮音大钢琴.zip",
|
||||
10: "10.Music_Box_八音盒.zip",
|
||||
100: "100.FX_brightness_合成特效-亮音.zip",
|
||||
101: "101.FX_goblins_合成特效-小妖.zip",
|
||||
102: "102.FX_echoes_合成特效-回声.zip",
|
||||
103: "103.FX_sci-fi_合成特效-科幻.zip",
|
||||
104: "104.Sitar_锡塔尔.zip",
|
||||
105: "105.Banjo_班卓.zip",
|
||||
106: "106.Shamisen_三味线.zip",
|
||||
107: "107.Koto_筝.zip",
|
||||
108: "108.Kalimba_卡林巴.zip",
|
||||
109: "109.Bagpipe_风笛.zip",
|
||||
11: "11.Vibraphone_电颤琴.zip",
|
||||
110: "110.Fiddle_古提琴.zip",
|
||||
111: "111.Shanai_唢呐.zip",
|
||||
112: "112.Tinkle_Bell_铃铛.zip",
|
||||
113: "113.Agogo_拉丁打铃.zip",
|
||||
114: "114.Steel_Drums_钢鼓.zip",
|
||||
115: "115.Woodblock_木块.zip",
|
||||
116: "116.Taiko_Drum_太鼓.zip",
|
||||
117: "117.Melodic_Tom_嗵鼓.zip",
|
||||
118: "118.Synth_Drum_合成鼓.zip",
|
||||
119: "119.Reverse_Cymbal_镲波形反转.zip",
|
||||
12: "12.Marimba_马林巴.zip",
|
||||
13: "13.Xylophone_木琴.zip",
|
||||
14: "14.Tubular_Bells_管钟.zip",
|
||||
15: "15.Dulcimer_扬琴.zip",
|
||||
16: "16.Drawbar_Organ_击杆风琴.zip",
|
||||
17: "17.Percussive_Organ_打击型风琴.zip",
|
||||
18: "18.Rock_Organ_摇滚风琴.zip",
|
||||
19: "19.Church_Organ_管风琴.zip",
|
||||
2: "2.Electric_Grand_Piano_电子大钢琴.zip",
|
||||
20: "20.Reed_Organ_簧风琴.zip",
|
||||
21: "21.Accordion_手风琴.zip",
|
||||
22: "22.Harmonica_口琴.zip",
|
||||
23: "23.Tango_Accordian_探戈手风琴.zip",
|
||||
24: "24.Acoustic_Guitar_(nylon)_尼龙弦吉他.zip",
|
||||
25: "25.Acoustic_Guitar(steel)_钢弦吉他.zip",
|
||||
26: "26.Electric_Guitar_(jazz)_爵士乐电吉他.zip",
|
||||
27: "27.Electric_Guitar_(clean)_清音电吉他.zip",
|
||||
28: "28.Electric_Guitar_(muted)_弱音电吉他.zip",
|
||||
29: "29.Overdriven_Guitar_驱动音效吉他.zip",
|
||||
3: "3.Honky-Tonk_Piano_酒吧钢琴.zip",
|
||||
30: "30.Distortion_Guitar_失真音效吉他.zip",
|
||||
31: "31.Guitar_Harmonics_吉他泛音.zip",
|
||||
32: "32.Acoustic_Bass_原声贝司.zip",
|
||||
33: "33.Electric_Bass(finger)_指拨电贝司.zip",
|
||||
34: "34.Electric_Bass(pick)_拨片拨电贝司.zip",
|
||||
35: "35.Fretless_Bass_无品贝司.zip",
|
||||
36: "36.Slap_Bass_A_击弦贝司A.zip",
|
||||
37: "37.Slap_Bass_B_击弦贝司B.zip",
|
||||
38: "38.Synth_Bass_A_合成贝司A.zip",
|
||||
39: "39.Synth_Bass_B_合成贝司B.zip",
|
||||
4: "4.Electric_Piano_1_电钢琴A.zip",
|
||||
40: "40.Violin_小提琴.zip",
|
||||
41: "41.Viola_中提琴.zip",
|
||||
42: "42.Cello_大提琴.zip",
|
||||
43: "43.Contrabass_低音提琴.zip",
|
||||
44: "44.Tremolo_Strings_弦乐震音.zip",
|
||||
45: "45.Pizzicato_Strings_弦乐拨奏.zip",
|
||||
46: "46.Orchestral_Harp_竖琴.zip",
|
||||
47: "47.Timpani_定音鼓.zip",
|
||||
48: "48.String_Ensemble_A_弦乐合奏A.zip",
|
||||
49: "49.String_Ensemble_B_弦乐合奏B.zip",
|
||||
5: "5.Electric_Piano_2_电钢琴B.zip",
|
||||
50: "50.SynthStrings_A_合成弦乐A.zip",
|
||||
51: "51.SynthStrings_B_合成弦乐B.zip",
|
||||
52: "52.Choir_Aahs_合唱“啊”音.zip",
|
||||
53: "53.Voice_Oohs_人声“哦”音.zip",
|
||||
54: "54.Synth_Voice_合成人声.zip",
|
||||
55: "55.Orchestra_Hit_乐队打击乐.zip",
|
||||
56: "56.Trumpet_小号.zip",
|
||||
57: "57.Trombone_长号.zip",
|
||||
58: "58.Tuba_大号.zip",
|
||||
59: "59.Muted_Trumpet_弱音小号.zip",
|
||||
6: "6.Harpsichord_拨弦古钢琴.zip",
|
||||
60: "60.French_Horn_圆号.zip",
|
||||
61: "61.Brass_Section_铜管组.zip",
|
||||
62: "62.Synth_Brass_A_合成铜管A.zip",
|
||||
63: "63.Synth_Brass_A_合成铜管B.zip",
|
||||
64: "64.Soprano_Sax_高音萨克斯.zip",
|
||||
65: "65.Alto_Sax_中音萨克斯.zip",
|
||||
66: "66.Tenor_Sax_次中音萨克斯.zip",
|
||||
67: "67.Baritone_Sax_上低音萨克斯.zip",
|
||||
68: "68.Oboe_双簧管.zip",
|
||||
69: "69.English_Horn_英国管.zip",
|
||||
7: "7.Clavinet_击弦古钢琴.zip",
|
||||
70: "70.Bassoon_大管.zip",
|
||||
71: "71.Clarinet_单簧管.zip",
|
||||
72: "72.Piccolo_短笛.zip",
|
||||
73: "73.Flute_长笛.zip",
|
||||
74: "74.Recorder_竖笛.zip",
|
||||
75: "75.Pan_Flute_排笛.zip",
|
||||
76: "76.Bottle_Blow_吹瓶口.zip",
|
||||
77: "77.Skakuhachi_尺八.zip",
|
||||
78: "78.Whistle_哨.zip",
|
||||
79: "79.Ocarina_洋埙.zip",
|
||||
8: "8.Celesta_钢片琴.zip",
|
||||
80: "80.Lead_square_合成主音-方波.zip",
|
||||
81: "81.Lead_sawtooth_合成主音-锯齿波.zip",
|
||||
82: "82.Lead_calliope_lead_合成主音-汽笛风琴.zip",
|
||||
83: "83.Lead_chiff_lead_合成主音-吹管.zip",
|
||||
84: "84.Lead_charang_合成主音5-吉他.zip",
|
||||
85: "85.Lead_voice_合成主音-人声.zip",
|
||||
86: "86.Lead_fifths_合成主音-五度.zip",
|
||||
87: "87.Lead_bass+lead_合成主音-低音加主音.zip",
|
||||
88: "88.Pad_new_age_合成柔音-新时代.zip",
|
||||
89: "89.Pad_warm_合成柔音-暖音.zip",
|
||||
9: "9.Glockenspiel_钟琴.zip",
|
||||
90: "90.Pad_polysynth_合成柔音-复合成.zip",
|
||||
91: "91.Pad_choir_合成柔音-合唱.zip",
|
||||
92: "92.Pad_bowed_合成柔音-弓弦.zip",
|
||||
93: "93.Pad_metallic_合成柔音-金属.zip",
|
||||
94: "94.Pad_halo_合成柔音-光环.zip",
|
||||
95: "95.Pad_sweep_合成柔音-扫弦.zip",
|
||||
96: "96.FX_rain_合成特效-雨.zip",
|
||||
97: "97.FX_soundtrack_合成特效-音轨.zip",
|
||||
98: "98.FX_crystal_合成特效-水晶.zip",
|
||||
99: "99.FX_atmosphere_合成特效-大气.zip",
|
||||
}
|
||||
|
||||
mcpack_name = {
|
||||
-1: "-1.Acoustic_Kit_打击乐.mcpack",
|
||||
0: "0.Acoustic_Grand_Piano_大钢琴.mcpack",
|
||||
1: "1.Bright_Acoustic_Piano_亮音大钢琴.mcpack",
|
||||
10: "10.Music_Box_八音盒.mcpack",
|
||||
100: "100.FX_brightness_合成特效-亮音.mcpack",
|
||||
101: "101.FX_goblins_合成特效-小妖.mcpack",
|
||||
102: "102.FX_echoes_合成特效-回声.mcpack",
|
||||
103: "103.FX_sci-fi_合成特效-科幻.mcpack",
|
||||
104: "104.Sitar_锡塔尔.mcpack",
|
||||
105: "105.Banjo_班卓.mcpack",
|
||||
106: "106.Shamisen_三味线.mcpack",
|
||||
107: "107.Koto_筝.mcpack",
|
||||
108: "108.Kalimba_卡林巴.mcpack",
|
||||
109: "109.Bagpipe_风笛.mcpack",
|
||||
11: "11.Vibraphone_电颤琴.mcpack",
|
||||
110: "110.Fiddle_古提琴.mcpack",
|
||||
111: "111.Shanai_唢呐.mcpack",
|
||||
112: "112.Tinkle_Bell_铃铛.mcpack",
|
||||
113: "113.Agogo_拉丁打铃.mcpack",
|
||||
114: "114.Steel_Drums_钢鼓.mcpack",
|
||||
115: "115.Woodblock_木块.mcpack",
|
||||
116: "116.Taiko_Drum_太鼓.mcpack",
|
||||
117: "117.Melodic_Tom_嗵鼓.mcpack",
|
||||
118: "118.Synth_Drum_合成鼓.mcpack",
|
||||
119: "119.Reverse_Cymbal_镲波形反转.mcpack",
|
||||
12: "12.Marimba_马林巴.mcpack",
|
||||
13: "13.Xylophone_木琴.mcpack",
|
||||
14: "14.Tubular_Bells_管钟.mcpack",
|
||||
15: "15.Dulcimer_扬琴.mcpack",
|
||||
16: "16.Drawbar_Organ_击杆风琴.mcpack",
|
||||
17: "17.Percussive_Organ_打击型风琴.mcpack",
|
||||
18: "18.Rock_Organ_摇滚风琴.mcpack",
|
||||
19: "19.Church_Organ_管风琴.mcpack",
|
||||
2: "2.Electric_Grand_Piano_电子大钢琴.mcpack",
|
||||
20: "20.Reed_Organ_簧风琴.mcpack",
|
||||
21: "21.Accordion_手风琴.mcpack",
|
||||
22: "22.Harmonica_口琴.mcpack",
|
||||
23: "23.Tango_Accordian_探戈手风琴.mcpack",
|
||||
24: "24.Acoustic_Guitar_(nylon)_尼龙弦吉他.mcpack",
|
||||
25: "25.Acoustic_Guitar(steel)_钢弦吉他.mcpack",
|
||||
26: "26.Electric_Guitar_(jazz)_爵士乐电吉他.mcpack",
|
||||
27: "27.Electric_Guitar_(clean)_清音电吉他.mcpack",
|
||||
28: "28.Electric_Guitar_(muted)_弱音电吉他.mcpack",
|
||||
29: "29.Overdriven_Guitar_驱动音效吉他.mcpack",
|
||||
3: "3.Honky-Tonk_Piano_酒吧钢琴.mcpack",
|
||||
30: "30.Distortion_Guitar_失真音效吉他.mcpack",
|
||||
31: "31.Guitar_Harmonics_吉他泛音.mcpack",
|
||||
32: "32.Acoustic_Bass_原声贝司.mcpack",
|
||||
33: "33.Electric_Bass(finger)_指拨电贝司.mcpack",
|
||||
34: "34.Electric_Bass(pick)_拨片拨电贝司.mcpack",
|
||||
35: "35.Fretless_Bass_无品贝司.mcpack",
|
||||
36: "36.Slap_Bass_A_击弦贝司A.mcpack",
|
||||
37: "37.Slap_Bass_B_击弦贝司B.mcpack",
|
||||
38: "38.Synth_Bass_A_合成贝司A.mcpack",
|
||||
39: "39.Synth_Bass_B_合成贝司B.mcpack",
|
||||
4: "4.Electric_Piano_1_电钢琴A.mcpack",
|
||||
40: "40.Violin_小提琴.mcpack",
|
||||
41: "41.Viola_中提琴.mcpack",
|
||||
42: "42.Cello_大提琴.mcpack",
|
||||
43: "43.Contrabass_低音提琴.mcpack",
|
||||
44: "44.Tremolo_Strings_弦乐震音.mcpack",
|
||||
45: "45.Pizzicato_Strings_弦乐拨奏.mcpack",
|
||||
46: "46.Orchestral_Harp_竖琴.mcpack",
|
||||
47: "47.Timpani_定音鼓.mcpack",
|
||||
48: "48.String_Ensemble_A_弦乐合奏A.mcpack",
|
||||
49: "49.String_Ensemble_B_弦乐合奏B.mcpack",
|
||||
5: "5.Electric_Piano_2_电钢琴B.mcpack",
|
||||
50: "50.SynthStrings_A_合成弦乐A.mcpack",
|
||||
51: "51.SynthStrings_B_合成弦乐B.mcpack",
|
||||
52: "52.Choir_Aahs_合唱“啊”音.mcpack",
|
||||
53: "53.Voice_Oohs_人声“哦”音.mcpack",
|
||||
54: "54.Synth_Voice_合成人声.mcpack",
|
||||
55: "55.Orchestra_Hit_乐队打击乐.mcpack",
|
||||
56: "56.Trumpet_小号.mcpack",
|
||||
57: "57.Trombone_长号.mcpack",
|
||||
58: "58.Tuba_大号.mcpack",
|
||||
59: "59.Muted_Trumpet_弱音小号.mcpack",
|
||||
6: "6.Harpsichord_拨弦古钢琴.mcpack",
|
||||
60: "60.French_Horn_圆号.mcpack",
|
||||
61: "61.Brass_Section_铜管组.mcpack",
|
||||
62: "62.Synth_Brass_A_合成铜管A.mcpack",
|
||||
63: "63.Synth_Brass_A_合成铜管B.mcpack",
|
||||
64: "64.Soprano_Sax_高音萨克斯.mcpack",
|
||||
65: "65.Alto_Sax_中音萨克斯.mcpack",
|
||||
66: "66.Tenor_Sax_次中音萨克斯.mcpack",
|
||||
67: "67.Baritone_Sax_上低音萨克斯.mcpack",
|
||||
68: "68.Oboe_双簧管.mcpack",
|
||||
69: "69.English_Horn_英国管.mcpack",
|
||||
7: "7.Clavinet_击弦古钢琴.mcpack",
|
||||
70: "70.Bassoon_大管.mcpack",
|
||||
71: "71.Clarinet_单簧管.mcpack",
|
||||
72: "72.Piccolo_短笛.mcpack",
|
||||
73: "73.Flute_长笛.mcpack",
|
||||
74: "74.Recorder_竖笛.mcpack",
|
||||
75: "75.Pan_Flute_排笛.mcpack",
|
||||
76: "76.Bottle_Blow_吹瓶口.mcpack",
|
||||
77: "77.Skakuhachi_尺八.mcpack",
|
||||
78: "78.Whistle_哨.mcpack",
|
||||
79: "79.Ocarina_洋埙.mcpack",
|
||||
8: "8.Celesta_钢片琴.mcpack",
|
||||
80: "80.Lead_square_合成主音-方波.mcpack",
|
||||
81: "81.Lead_sawtooth_合成主音-锯齿波.mcpack",
|
||||
82: "82.Lead_calliope_lead_合成主音-汽笛风琴.mcpack",
|
||||
83: "83.Lead_chiff_lead_合成主音-吹管.mcpack",
|
||||
84: "84.Lead_charang_合成主音5-吉他.mcpack",
|
||||
85: "85.Lead_voice_合成主音-人声.mcpack",
|
||||
86: "86.Lead_fifths_合成主音-五度.mcpack",
|
||||
87: "87.Lead_bass+lead_合成主音-低音加主音.mcpack",
|
||||
88: "88.Pad_new_age_合成柔音-新时代.mcpack",
|
||||
89: "89.Pad_warm_合成柔音-暖音.mcpack",
|
||||
9: "9.Glockenspiel_钟琴.mcpack",
|
||||
90: "90.Pad_polysynth_合成柔音-复合成.mcpack",
|
||||
91: "91.Pad_choir_合成柔音-合唱.mcpack",
|
||||
92: "92.Pad_bowed_合成柔音-弓弦.mcpack",
|
||||
93: "93.Pad_metallic_合成柔音-金属.mcpack",
|
||||
94: "94.Pad_halo_合成柔音-光环.mcpack",
|
||||
95: "95.Pad_sweep_合成柔音-扫弦.mcpack",
|
||||
96: "96.FX_rain_合成特效-雨.mcpack",
|
||||
97: "97.FX_soundtrack_合成特效-音轨.mcpack",
|
||||
98: "98.FX_crystal_合成特效-水晶.mcpack",
|
||||
99: "99.FX_atmosphere_合成特效-大气.mcpack",
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(zip_name[0])
|
||||
134
old-things/bgArrayLib/pitchStrConstant.py
Normal file
134
old-things/bgArrayLib/pitchStrConstant.py
Normal file
@@ -0,0 +1,134 @@
|
||||
pitch = {
|
||||
"0": "0.0220970869120796",
|
||||
"1": "0.0234110480761981",
|
||||
"2": "0.0248031414370031",
|
||||
"3": "0.0262780129766786",
|
||||
"4": "0.0278405849418856",
|
||||
"5": "0.0294960722713029",
|
||||
"6": "0.03125",
|
||||
"7": "0.033108221698728",
|
||||
"8": "0.0350769390096679",
|
||||
"9": "0.037162722343835",
|
||||
"10": "0.0393725328092148",
|
||||
"11": "0.0417137454428136",
|
||||
"12": "0.0441941738241592",
|
||||
"13": "0.0468220961523963",
|
||||
"14": "0.0496062828740062",
|
||||
"15": "0.0525560259533572",
|
||||
"16": "0.0556811698837712",
|
||||
"17": "0.0589921445426059",
|
||||
"18": "0.0625",
|
||||
"19": "0.066216443397456",
|
||||
"20": "0.0701538780193358",
|
||||
"21": "0.0743254446876701",
|
||||
"22": "0.0787450656184296",
|
||||
"23": "0.0834274908856271",
|
||||
"24": "0.0883883476483184",
|
||||
"25": "0.0936441923047926",
|
||||
"26": "0.0992125657480125",
|
||||
"27": "0.105112051906714",
|
||||
"28": "0.111362339767542",
|
||||
"29": "0.117984289085212",
|
||||
"30": "0.125",
|
||||
"31": "0.132432886794912",
|
||||
"32": "0.140307756038672",
|
||||
"33": "0.14865088937534",
|
||||
"34": "0.157490131236859",
|
||||
"35": "0.166854981771254",
|
||||
"36": "0.176776695296637",
|
||||
"37": "0.187288384609585",
|
||||
"38": "0.198425131496025",
|
||||
"39": "0.210224103813429",
|
||||
"40": "0.222724679535085",
|
||||
"41": "0.235968578170423",
|
||||
"42": "0.25",
|
||||
"43": "0.264865773589824",
|
||||
"44": "0.280615512077343",
|
||||
"45": "0.29730177875068",
|
||||
"46": "0.314980262473718",
|
||||
"47": "0.333709963542509",
|
||||
"48": "0.353553390593274",
|
||||
"49": "0.37457676921917",
|
||||
"50": "0.39685026299205",
|
||||
"51": "0.420448207626857",
|
||||
"52": "0.44544935907017",
|
||||
"53": "0.471937156340847",
|
||||
"54": "0.5",
|
||||
"55": "0.529731547179648",
|
||||
"56": "0.561231024154687",
|
||||
"57": "0.594603557501361",
|
||||
"58": "0.629960524947437",
|
||||
"59": "0.667419927085017",
|
||||
"60": "0.707106781186548",
|
||||
"61": "0.749153538438341",
|
||||
"62": "0.7937005259841",
|
||||
"63": "0.840896415253715",
|
||||
"64": "0.890898718140339",
|
||||
"65": "0.943874312681694",
|
||||
"66": "1",
|
||||
"67": "1.0594630943593",
|
||||
"68": "1.12246204830937",
|
||||
"69": "1.18920711500272",
|
||||
"70": "1.25992104989487",
|
||||
"71": "1.33483985417003",
|
||||
"72": "1.4142135623731",
|
||||
"73": "1.49830707687668",
|
||||
"74": "1.5874010519682",
|
||||
"75": "1.68179283050743",
|
||||
"76": "1.78179743628068",
|
||||
"77": "1.88774862536339",
|
||||
"78": "2",
|
||||
"79": "2.11892618871859",
|
||||
"80": "2.24492409661875",
|
||||
"81": "2.37841423000544",
|
||||
"82": "2.51984209978975",
|
||||
"83": "2.66967970834007",
|
||||
"84": "2.82842712474619",
|
||||
"85": "2.99661415375336",
|
||||
"86": "3.1748021039364",
|
||||
"87": "3.36358566101486",
|
||||
"88": "3.56359487256136",
|
||||
"89": "3.77549725072677",
|
||||
"90": "4",
|
||||
"91": "4.23785237743718",
|
||||
"92": "4.48984819323749",
|
||||
"93": "4.75682846001088",
|
||||
"94": "5.03968419957949",
|
||||
"95": "5.33935941668014",
|
||||
"96": "5.65685424949238",
|
||||
"97": "5.99322830750673",
|
||||
"98": "6.3496042078728",
|
||||
"99": "6.72717132202972",
|
||||
"100": "7.12718974512272",
|
||||
"101": "7.55099450145355",
|
||||
"102": "8",
|
||||
"103": "8.47570475487436",
|
||||
"104": "8.97969638647498",
|
||||
"105": "9.51365692002177",
|
||||
"106": "10.079368399159",
|
||||
"107": "10.6787188333603",
|
||||
"108": "11.3137084989848",
|
||||
"109": "11.9864566150135",
|
||||
"110": "12.6992084157456",
|
||||
"111": "13.4543426440594",
|
||||
"112": "14.2543794902454",
|
||||
"113": "15.1019890029071",
|
||||
"114": "16",
|
||||
"115": "16.9514095097487",
|
||||
"116": "17.95939277295",
|
||||
"117": "19.0273138400435",
|
||||
"118": "20.158736798318",
|
||||
"119": "21.3574376667206",
|
||||
"120": "22.6274169979695",
|
||||
"121": "23.9729132300269",
|
||||
"122": "25.3984168314912",
|
||||
"123": "26.9086852881189",
|
||||
"124": "28.5087589804909",
|
||||
"125": "30.2039780058142",
|
||||
"126": "32",
|
||||
"127": "33.9028190194974",
|
||||
"128": "35.9187855458999",
|
||||
"129": "38.0546276800871",
|
||||
"130": "40.3174735966359",
|
||||
"131": "42.7148753334411",
|
||||
}
|
||||
208
old-things/bgArrayLib/reader.py
Normal file
208
old-things/bgArrayLib/reader.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# from nmcsup.log import log
|
||||
import pickle
|
||||
|
||||
|
||||
class Note:
|
||||
def __init__(self, channel, pitch, velocity, time, time_position, instrument):
|
||||
self.channel = channel
|
||||
self.pitch = pitch
|
||||
self.velocity = velocity
|
||||
self.delay = time
|
||||
self.time_position = time_position
|
||||
self.instrument = instrument
|
||||
self.CD = "d"
|
||||
|
||||
def get_CD(self, start, end):
|
||||
if end - start > 1.00:
|
||||
self.CD = "c"
|
||||
else:
|
||||
self.CD = "d"
|
||||
|
||||
|
||||
def midiNewReader(midfile: str):
|
||||
import mido
|
||||
|
||||
# from msctspt.threadOpera import NewThread
|
||||
from bgArrayLib.bpm import get
|
||||
|
||||
def Time(mt, tpb_a, bpm_a):
|
||||
return round(mt / tpb_a / bpm_a * 60 * 20)
|
||||
|
||||
Notes = []
|
||||
tracks = []
|
||||
note_list = []
|
||||
close = []
|
||||
on = []
|
||||
off = []
|
||||
instruments = []
|
||||
isPercussion = False
|
||||
try:
|
||||
mid = mido.MidiFile(midfile)
|
||||
except Exception:
|
||||
print("找不到文件或无法读取文件" + midfile)
|
||||
return False
|
||||
tpb = mid.ticks_per_beat
|
||||
bpm = get(mid)
|
||||
# 解析
|
||||
# def loadMidi(track1):
|
||||
for track in mid.tracks:
|
||||
overallTime = 0.0
|
||||
instrument = 0
|
||||
for i in track:
|
||||
overallTime += i.time
|
||||
try:
|
||||
if i.channel != 9:
|
||||
# try:
|
||||
# log("event_type(事件): " + str(i.type) + " channel(音轨): " + str(i.channel) +
|
||||
# " note/pitch(音高): " +
|
||||
# str(i[2]) +
|
||||
# " velocity(力度): " + str(i.velocity) + " time(间隔时间): " + str(i.time) +
|
||||
# " overallTime/globalTime/timePosition: " + str(overallTime) + " \n")
|
||||
# except AttributeError:
|
||||
# log("event_type(事件): " + str(i.type) + " thing(内容):" + str(i) + " \n")
|
||||
if "program_change" in str(i):
|
||||
instrument = i.program
|
||||
if instrument > 119: # 音色不够
|
||||
pass
|
||||
else:
|
||||
instruments.append(i.program)
|
||||
if "note_on" in str(i) and i.velocity > 0:
|
||||
print(i)
|
||||
# print(i.note)
|
||||
# print([Note(i.channel, i.note, i.velocity, i.time, Time(overallTime, tpb, bpm), instrument)])
|
||||
tracks.append(
|
||||
[
|
||||
Note(
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
instrument,
|
||||
)
|
||||
]
|
||||
)
|
||||
note_list.append(
|
||||
[
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
instrument,
|
||||
]
|
||||
)
|
||||
on.append([i.note, Time(overallTime, tpb, bpm)])
|
||||
# return [Note(i.channel, i, i.velocity, i.time, Time(overallTime, tpb, bpm))]
|
||||
if "note_off" in str(i) or "note_on" in str(i) and i.velocity == 0:
|
||||
# print(i)
|
||||
# print([Note(i.channel, i.note, i.velocity, i.time, Time(overallTime, tpb, bpm))])
|
||||
close.append(
|
||||
[
|
||||
Note(
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
instrument,
|
||||
)
|
||||
]
|
||||
)
|
||||
off.append([i.note, Time(overallTime, tpb, bpm)])
|
||||
# return [Note(i.channel, i, i.velocity, i.time, Time(overallTime, tpb, bpm))]
|
||||
except AttributeError:
|
||||
pass
|
||||
if "note_on" in str(i) and i.channel == 9:
|
||||
if "note_on" in str(i) and i.velocity > 0:
|
||||
print(i)
|
||||
# print(i.note)
|
||||
# print([Note(i.channel, i.note, i.velocity, i.time, Time(overallTime, tpb, bpm), -1)])
|
||||
tracks.append(
|
||||
[
|
||||
Note(
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
-1,
|
||||
)
|
||||
]
|
||||
)
|
||||
note_list.append(
|
||||
[
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
-1,
|
||||
]
|
||||
)
|
||||
on.append([i.note, Time(overallTime, tpb, bpm)])
|
||||
isPercussion = True
|
||||
# return [Note(i.channel, i, i.velocity, i.time, Time(overallTime, tpb, bpm))]
|
||||
Notes.append(tracks)
|
||||
if instruments is []:
|
||||
instruments.append(0)
|
||||
instruments = list(set(instruments))
|
||||
with open("1.pkl", "wb") as b:
|
||||
pickle.dump([instruments, isPercussion], b)
|
||||
|
||||
# for j, track in enumerate(mid.tracks):
|
||||
# th = NewThread(loadMidi, (track,))
|
||||
# th.start()
|
||||
# Notes.append(th.getResult())
|
||||
|
||||
# print(Notes)
|
||||
print(Notes.__len__())
|
||||
# print(note_list)
|
||||
print(instruments)
|
||||
return Notes
|
||||
# return [Notes, note_list]
|
||||
|
||||
|
||||
def midiClassReader(midfile: str):
|
||||
import mido
|
||||
|
||||
from bgArrayLib.bpm import get
|
||||
|
||||
def Time(mt, tpb_a, bpm_a):
|
||||
return round(mt / tpb_a / bpm_a * 60 * 20)
|
||||
|
||||
Notes = []
|
||||
tracks = []
|
||||
try:
|
||||
mid = mido.MidiFile(filename=midfile, clip=True)
|
||||
except Exception:
|
||||
print("找不到文件或无法读取文件" + midfile)
|
||||
return False
|
||||
print("midi已经载入了。")
|
||||
tpb = mid.ticks_per_beat
|
||||
bpm = get(mid)
|
||||
for track in mid.tracks:
|
||||
overallTime = 0.0
|
||||
instrument = 0
|
||||
for i in track:
|
||||
overallTime += i.time
|
||||
if "note_on" in str(i) and i.velocity > 0:
|
||||
print(i)
|
||||
tracks.append(
|
||||
[
|
||||
Note(
|
||||
i.channel,
|
||||
i.note,
|
||||
i.velocity,
|
||||
i.time,
|
||||
Time(overallTime, tpb, bpm),
|
||||
instrument,
|
||||
)
|
||||
]
|
||||
)
|
||||
Notes.append(tracks)
|
||||
print(Notes.__len__())
|
||||
return Notes
|
||||
147
old-things/bgArrayLib/sy_resourcesPacker.py
Normal file
147
old-things/bgArrayLib/sy_resourcesPacker.py
Normal file
@@ -0,0 +1,147 @@
|
||||
import os
|
||||
import pickle
|
||||
import shutil
|
||||
|
||||
# import tkinter.filedialog
|
||||
# from namesConstant import zip_name
|
||||
# from namesConstant import mcpack_name
|
||||
import bgArrayLib.namesConstant
|
||||
|
||||
zipN = bgArrayLib.namesConstant.zip_name
|
||||
mpN = bgArrayLib.namesConstant.mcpack_name
|
||||
|
||||
manifest = {
|
||||
"format_version": 1,
|
||||
"header": {
|
||||
"name": "羽音缭绕-midiout_25.5--音创使用",
|
||||
"description": "羽音缭绕-midiout_25.0--音创使用",
|
||||
"uuid": "c1adbda4-3b3e-4e5b-a57e-cde8ac80ee19",
|
||||
"version": [25, 5, 0],
|
||||
},
|
||||
"modules": [
|
||||
{
|
||||
"description": "羽音缭绕-midiout_25.0--音创使用",
|
||||
"type": "resources",
|
||||
"uuid": "c13455d5-b9f3-47f2-9706-c05ad86b3180 ",
|
||||
"version": [25, 5, 0],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def resources_pathSetting(newPath: str = ""):
|
||||
if not os.path.isfile("./bgArrayLib/resourcesPath.rpposi") and newPath == "":
|
||||
return [False, 1] # 1:没有路径文件
|
||||
elif newPath != "": # not os.path.isfile("./bgArrayLib/resourcesPath.rpposi") and
|
||||
path = newPath
|
||||
print(path)
|
||||
with open("./bgArrayLib/resourcesPath.rpposi", "w") as w:
|
||||
w.write(path)
|
||||
if "mcpack(国际版推荐)格式_25.0" in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" in os.listdir(path):
|
||||
return [True, path, 1] # 1:都有
|
||||
elif "mcpack(国际版推荐)格式_25.0" in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" not in os.listdir(path):
|
||||
return [True, path, 2] # 2:有pack
|
||||
elif "mcpack(国际版推荐)格式_25.0" not in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" in os.listdir(path):
|
||||
return [True, path, 3] # 3:有zip
|
||||
else:
|
||||
return [False, 2] # 2:路径文件指示错误
|
||||
if os.path.isfile("./bgArrayLib/resourcesPath.rpposi") and newPath == "":
|
||||
with open("./bgArrayLib/resourcesPath.rpposi", "r") as f:
|
||||
path = f.read()
|
||||
if "mcpack(国际版推荐)格式_25.0" in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" in os.listdir(path):
|
||||
return [True, path, 1] # 1:都有
|
||||
elif "mcpack(国际版推荐)格式_25.0" in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" not in os.listdir(path):
|
||||
return [True, path, 2] # 2:有pack
|
||||
elif "mcpack(国际版推荐)格式_25.0" not in os.listdir(
|
||||
path
|
||||
) and "zip格式_25.0" in os.listdir(path):
|
||||
return [True, path, 3] # 3:有zip
|
||||
else:
|
||||
return [False, 2] # 2:路径文件指示错误
|
||||
|
||||
raise
|
||||
|
||||
|
||||
def choose_resources():
|
||||
global zipN
|
||||
global mpN
|
||||
back_list = []
|
||||
try:
|
||||
with open(r"1.pkl", "rb") as rb:
|
||||
instrument = list(pickle.load(rb))
|
||||
print(instrument)
|
||||
except FileNotFoundError:
|
||||
with open(r"./nmcsup/1.pkl", "rb") as rb:
|
||||
instrument = list(pickle.load(rb))
|
||||
print(instrument)
|
||||
path = resources_pathSetting()
|
||||
if path.__len__() == 2:
|
||||
return path
|
||||
else:
|
||||
dataT = path[2]
|
||||
pathT = path[1]
|
||||
if dataT == 1:
|
||||
if instrument[1] is True: # 是否存在打击乐器
|
||||
index = zipN.get(-1, "")
|
||||
percussion_instrument = str(pathT) + "\\zip格式_25.0\\" + index
|
||||
# print(percussion_instrument)
|
||||
back_list.append(percussion_instrument)
|
||||
for i in instrument[0]:
|
||||
ins_p = str(pathT) + "\\zip格式_25.0\\" + str(zipN.get(i))
|
||||
# print(ins_p)
|
||||
back_list.append(ins_p)
|
||||
print(back_list)
|
||||
return back_list
|
||||
elif dataT == 2:
|
||||
if instrument[1] is True:
|
||||
index = mpN.get(-1, "")
|
||||
percussion_instrument = (
|
||||
str(pathT) + "\\mcpack(国际版推荐)格式_25.0\\" + index
|
||||
)
|
||||
# print(percussion_instrument)
|
||||
back_list.append(percussion_instrument)
|
||||
for i in instrument[0]:
|
||||
ins_p = str(pathT) + "\\mcpack(国际版推荐)格式_25.0\\" + str(mpN.get(i))
|
||||
# print(ins_p)
|
||||
back_list.append(ins_p)
|
||||
print(back_list)
|
||||
return back_list
|
||||
elif dataT == 3:
|
||||
if instrument[1] is True:
|
||||
index = zipN.get(-1, "")
|
||||
percussion_instrument = str(pathT) + "\\zip格式_25.0\\" + index
|
||||
# print(percussion_instrument)
|
||||
back_list.append(percussion_instrument)
|
||||
for i in instrument[0]:
|
||||
ins_p = str(pathT) + "\\zip格式_25.0\\" + str(zipN.get(i))
|
||||
# print(ins_p)
|
||||
back_list.append(ins_p)
|
||||
print(back_list)
|
||||
return back_list
|
||||
raise
|
||||
|
||||
|
||||
def scatteredPack(path):
|
||||
pack_list = choose_resources()
|
||||
print(pack_list)
|
||||
print(path)
|
||||
# os.close("L:/0WorldMusicCreater-MFMS new edition")
|
||||
# shutil.copy("L:\\shenyu\\音源的资源包\\羽音缭绕-midiout_25.0\\mcpack(国际版推荐)格式_25.0\\0.Acoustic_Grand_Piano_大钢琴.mcpack",
|
||||
# "L:/0WorldMusicCreater-MFMS new edition")
|
||||
for i in pack_list:
|
||||
shutil.copy(i, path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# print(resources_pathSetting(r"L:\shenyu\音源的资源包\羽音缭绕-midiout_25.0"))
|
||||
choose_resources()
|
||||
1
old-things/fcwslib/LICENSE
Normal file
1
old-things/fcwslib/LICENSE
Normal file
@@ -0,0 +1 @@
|
||||
SEE: https://mingfengpigeon.mit-license.org/
|
||||
5
old-things/fcwslib/__init__.py
Normal file
5
old-things/fcwslib/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
__all__ = ['Server', 'Plugin', 'build_header']
|
||||
__version__ = '3.0.1'
|
||||
__author__ = ['mingfengpigeon <mingfengpigeon@gmail.com>',"Eilles Wan <EillesWan@outlook.com>"]
|
||||
|
||||
from .server import Server, Plugin, build_header
|
||||
142
old-things/fcwslib/server.py
Normal file
142
old-things/fcwslib/server.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import uuid
|
||||
|
||||
import websockets
|
||||
|
||||
|
||||
class Server:
|
||||
sent_commands = {}
|
||||
subscribed_events = {}
|
||||
_plugins = []
|
||||
_connections = []
|
||||
|
||||
def __init__(self, server='0.0.0.0', port=8000, debug_mode=False):
|
||||
self._server = server
|
||||
self._port = port
|
||||
self._debug_mode = debug_mode
|
||||
|
||||
def handler(self):
|
||||
return copy.deepcopy(self._plugins)
|
||||
|
||||
def add_plugin(self, plugin):
|
||||
if self._plugins:
|
||||
for connection in self._connections:
|
||||
plugin_ = plugin()
|
||||
asyncio.create_task(plugin_.on_connect())
|
||||
connection.append(plugin_)
|
||||
self._plugins.append(plugin)
|
||||
|
||||
def remove_plugin(self, plugin):
|
||||
if self._connections:
|
||||
for connection in self._connections:
|
||||
for plugin_ in connection.plugins:
|
||||
if isinstance(plugin_, plugin):
|
||||
plugin_.remove(plugin_)
|
||||
break
|
||||
self._plugins.remove(plugin)
|
||||
|
||||
async def run_forever(self):
|
||||
self.running = True
|
||||
async with websockets.serve(self._on_connect, self._server, self._port):
|
||||
await asyncio.Future()
|
||||
|
||||
async def _on_connect(self, websocket, path):
|
||||
plugins = []
|
||||
self._connections.append({
|
||||
"websocket": websocket,
|
||||
"path": path,
|
||||
"plugins": plugins,
|
||||
})
|
||||
for plugin in self._plugins:
|
||||
plugins.append(plugin(websocket, path, self, self._debug_mode))
|
||||
for plugin in plugins:
|
||||
asyncio.create_task(plugin.on_connect())
|
||||
while self.running:
|
||||
try:
|
||||
response = json.loads(await websocket.recv())
|
||||
except (websockets.exceptions.ConnectionClosedOK, websockets.exceptions.ConnectionClosedError):
|
||||
tasks = []
|
||||
for plugin in plugins:
|
||||
tasks.append(plugin.on_disconnect())
|
||||
for task in tasks:
|
||||
await task
|
||||
break
|
||||
else:
|
||||
message_purpose = response['header']['messagePurpose']
|
||||
if message_purpose == 'commandResponse':
|
||||
request_id = response['header']['requestId']
|
||||
if request_id in self.sent_commands:
|
||||
asyncio.create_task(self.sent_commands[request_id](response))
|
||||
del self.sent_commands[request_id]
|
||||
else:
|
||||
try:
|
||||
event_name = response['header']['eventName']
|
||||
asyncio.create_task(self.subscribed_events[event_name](response))
|
||||
except KeyError:
|
||||
print("ERROR EVENT NAME:\n{}".format(response))
|
||||
|
||||
async def disconnect(self, websocket: websockets.WebSocketServerProtocol):
|
||||
self.running = False
|
||||
await websocket.close_connection()
|
||||
for number in range(len(self._connections) - 1):
|
||||
connection = self._connections[number]
|
||||
if connection['websocket'] == websocket:
|
||||
del self._connections[number]
|
||||
|
||||
|
||||
class Plugin:
|
||||
def __init__(self, websocket, path, server, debug_mode=False):
|
||||
self._websocket = websocket
|
||||
self._path = path
|
||||
self._server = server
|
||||
self._debug_mode = debug_mode
|
||||
|
||||
async def on_connect(self):
|
||||
pass
|
||||
|
||||
async def on_disconnect(self):
|
||||
pass
|
||||
|
||||
async def on_receive(self, response):
|
||||
pass
|
||||
|
||||
async def send_command(self, command, callback=None):
|
||||
request = {
|
||||
'body': {'commandLine': command},
|
||||
'header': build_header('commandRequest')
|
||||
}
|
||||
if callback:
|
||||
self._server.sent_commands[request['header']['requestId']] = callback
|
||||
await self._websocket.send(json.dumps(request, **{'indent': 4} if self._debug_mode else {}))
|
||||
|
||||
async def subscribe(self, event_name, callback):
|
||||
request = {
|
||||
'body': {'eventName': event_name},
|
||||
'header': build_header('subscribe')
|
||||
}
|
||||
self._server.subscribed_events[event_name] = callback
|
||||
await self._websocket.send(json.dumps(request, **{'indent': 4} if self._debug_mode else {}))
|
||||
|
||||
async def unsubscribe(self, event_name):
|
||||
request = {
|
||||
'body': {'eventName': event_name},
|
||||
'header': build_header('unsubscribe')
|
||||
}
|
||||
del self._server.subscribed_events[event_name]
|
||||
await self._websocket.send(json.dumps(request, **{'indent': 4} if self._debug_mode else {}))
|
||||
|
||||
async def disconnect(self):
|
||||
await self._server.disconnect(self._websocket)
|
||||
|
||||
|
||||
def build_header(message_purpose, request_id=None):
|
||||
if not request_id:
|
||||
request_id = str(uuid.uuid4())
|
||||
return {
|
||||
'requestId': request_id,
|
||||
'messagePurpose': message_purpose,
|
||||
'version': '1',
|
||||
'messageType': 'commandRequest',
|
||||
}
|
||||
@@ -4,8 +4,8 @@
|
||||
requires-python = ">= 3.8, < 4.0"
|
||||
dependencies = [
|
||||
"mido >= 1.3",
|
||||
"tomli>=2.4.0; python_version < '3.11'",
|
||||
"tomli-w>=1.0.0",
|
||||
"tomli >= 2.4.0; python_version < '3.11'",
|
||||
"tomli-w >= 1.0.0",
|
||||
"xxhash >= 3",
|
||||
]
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# 注意,这里是作者署名文件,文件格式开头为单子启
|
||||
# 紧跟其后,不加空格留下常用名,常用名即常用网名
|
||||
# 而在其后是各个语言下的名字。用 井字符 开头表示
|
||||
# 注释,请注意,注释符号必须在一行之首否则无作用
|
||||
# 每进行一次分段表示一个新的开发者,换行表示一个
|
||||
# 新的语言。请全体开发者就此署名,谢谢!
|
||||
启金羿
|
||||
zh-CN 金羿
|
||||
zh-TW 金羿
|
||||
zh-ME 金羿羿喵
|
||||
zh-HK 金 羿
|
||||
en-GB Eilles
|
||||
en-US EillesWan
|
||||
|
||||
启诸葛亮与八卦阵
|
||||
zh-CN 诸葛亮与八卦阵
|
||||
zh-TW 諸葛亮與八卦陣
|
||||
zh-ME 诸葛八卦喵
|
||||
zh-HK 諸葛亮與八卦陣
|
||||
en-GB Bagua Array
|
||||
en-US bgArray
|
||||
@@ -1,47 +0,0 @@
|
||||
> 是谁把科技的领域布满政治的火药
|
||||
>
|
||||
> 是谁把纯净的蓝天染上暗淡的沉灰
|
||||
>
|
||||
> 中国人民无不热爱自己伟大的祖国
|
||||
>
|
||||
> 我们不会忘记屈辱历史留下的惨痛
|
||||
>
|
||||
> 我们希望世界和平
|
||||
>
|
||||
> 我们希望获得世界的尊重
|
||||
>
|
||||
> 愿世上再也没有战争
|
||||
>
|
||||
> 无论是热还是冷
|
||||
>
|
||||
> 无论是经济还是政治
|
||||
>
|
||||
> 让美妙的和平的优雅的音乐响彻世界
|
||||
>
|
||||
> ——金羿
|
||||
> 2022 5 7
|
||||
|
||||
|
||||
|
||||
> Who has dropped political gunpowder into the technology
|
||||
>
|
||||
> Who has dyed clear blue sky into the dark grey
|
||||
>
|
||||
> All Chinese people love our great homeland
|
||||
>
|
||||
> We *WILL* remember the remain pain of the humiliating history
|
||||
>
|
||||
> We love the whole world but in peace
|
||||
>
|
||||
> We love everyone but under respect
|
||||
>
|
||||
> It is to be hoped that the war ends forever
|
||||
>
|
||||
> Whatever it is cold or hot
|
||||
>
|
||||
> Whatever it is economical or political
|
||||
>
|
||||
> Just let the wonderful music of peace surround the world
|
||||
>
|
||||
> ---- Eilles
|
||||
> 7/5 2022
|
||||
@@ -11,4 +11,4 @@
|
||||
不得用于商业用途
|
||||
若 音·创 库被用于商业用途,应当将其剔除
|
||||
|
||||
版权所有 © 2025 诸葛亮与八卦阵
|
||||
版权所有 © 2026 玉衡Alioth
|
||||
18
test_read.py
Normal file
18
test_read.py
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
# 一个简单的项目实践测试
|
||||
from pathlib import Path
|
||||
from Musicreater import load_plugin_module, MusiCreater
|
||||
from Musicreater.plugins import _global_plugin_registry
|
||||
|
||||
load_plugin_module("Musicreater.builtin_plugins.midi_read")
|
||||
|
||||
print("当前支持的导入格式:", _global_plugin_registry.supported_input_formats())
|
||||
print("当前支持的导出格式:", _global_plugin_registry.supported_output_formats())
|
||||
|
||||
print(msct:=MusiCreater.import_music(Path("./resources/测试片段.mid")))
|
||||
|
||||
print(msct.music)
|
||||
|
||||
|
||||
|
||||
print(msct.midi_2_music_plugin.load(Path("./resources/测试片段.mid"), None))
|
||||
Reference in New Issue
Block a user