chippymu 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chippymu/__init__.py +0 -0
- chippymu/channelgen.py +85 -0
- chippymu/configs.py +28 -0
- chippymu/models.py +27 -0
- chippymu/sound.py +34 -0
- chippymu/utils.py +168 -0
- chippymu-0.1.0.dist-info/METADATA +8 -0
- chippymu-0.1.0.dist-info/RECORD +10 -0
- chippymu-0.1.0.dist-info/WHEEL +4 -0
- chippymu-0.1.0.dist-info/entry_points.txt +2 -0
chippymu/__init__.py
ADDED
|
File without changes
|
chippymu/channelgen.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
按通道生成音G
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from chippymu.utils import basic_wave_gen, basic_drum_gen, note_to_freq
|
|
8
|
+
|
|
9
|
+
from numpy import ndarray
|
|
10
|
+
from chippymu.configs import BasicParams
|
|
11
|
+
from chippymu.models import DrumType, WaveType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def generate_wave(
|
|
15
|
+
*, melody: list[tuple[float, int, float]], wave_type: WaveType, params: BasicParams
|
|
16
|
+
) -> ndarray:
|
|
17
|
+
"""
|
|
18
|
+
生成指定波形和旋律的通道
|
|
19
|
+
|
|
20
|
+
- 参数:
|
|
21
|
+
- melody: 旋律列表,每个元素为 (frequency, duration, amplitude)
|
|
22
|
+
- wave_type: 波形类型
|
|
23
|
+
- params: 基本参数
|
|
24
|
+
|
|
25
|
+
- 返回:
|
|
26
|
+
- 通道数据
|
|
27
|
+
"""
|
|
28
|
+
melody_beat: list[tuple[float, int, float]] = [
|
|
29
|
+
(start * params.beat_duration, note, dur * params.beat_duration)
|
|
30
|
+
for start, note, dur in melody
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
melody_audio = np.zeros(params.whole_duration)
|
|
34
|
+
|
|
35
|
+
for start, note, dur in melody_beat:
|
|
36
|
+
wave = basic_wave_gen(
|
|
37
|
+
wave_type=wave_type,
|
|
38
|
+
frequency=note_to_freq(note=note),
|
|
39
|
+
duration=dur,
|
|
40
|
+
sample_rate=params.sample_rate,
|
|
41
|
+
)
|
|
42
|
+
start_idx = int(start * params.sample_rate)
|
|
43
|
+
end_idx = start_idx + len(wave)
|
|
44
|
+
melody_audio[start_idx:end_idx] += wave
|
|
45
|
+
|
|
46
|
+
return melody_audio
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def generate_drums(
|
|
50
|
+
*, drums: list[tuple[float, str, float]], params: BasicParams
|
|
51
|
+
) -> ndarray:
|
|
52
|
+
"""
|
|
53
|
+
生成鼓声
|
|
54
|
+
|
|
55
|
+
- 参数:
|
|
56
|
+
- drums: 鼓声列表,每个元素是一个元组,包含起始时间,鼓声类型,持续时间
|
|
57
|
+
- params: 基础参数
|
|
58
|
+
|
|
59
|
+
- 返回:
|
|
60
|
+
- 通道数据
|
|
61
|
+
"""
|
|
62
|
+
drums_beat: list[tuple[float, str, float]] = [
|
|
63
|
+
(start * params.beat_duration, note, dur * params.beat_duration)
|
|
64
|
+
for start, note, dur in drums
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
drums_audio = np.zeros(params.whole_duration)
|
|
68
|
+
|
|
69
|
+
for start, note, dur in drums_beat:
|
|
70
|
+
try:
|
|
71
|
+
drum_type = DrumType(note)
|
|
72
|
+
except ValueError:
|
|
73
|
+
raise ValueError(f"无效的鼓类型名称: {note}")
|
|
74
|
+
|
|
75
|
+
wave = basic_drum_gen(
|
|
76
|
+
drum_type=drum_type,
|
|
77
|
+
duration=dur,
|
|
78
|
+
amplitude=1.0,
|
|
79
|
+
sample_rate=params.sample_rate,
|
|
80
|
+
)
|
|
81
|
+
start_idx = int(start * params.sample_rate)
|
|
82
|
+
end_idx = start_idx + len(wave)
|
|
83
|
+
drums_audio[start_idx:end_idx] += wave
|
|
84
|
+
|
|
85
|
+
return drums_audio
|
chippymu/configs.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
基础参数的配置文件
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BasicParams:
|
|
7
|
+
def __init__(self, *, sample_rate: int = 16000, bpm: int = 90, length: float):
|
|
8
|
+
# 定义采样率
|
|
9
|
+
self.sample_rate: int = sample_rate
|
|
10
|
+
# 定义BPM
|
|
11
|
+
self.bpm: int = bpm
|
|
12
|
+
# 定义音频总拍数
|
|
13
|
+
self.length: float = length
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def beat_duration(self) -> float:
|
|
17
|
+
"""
|
|
18
|
+
计算每拍的时长
|
|
19
|
+
"""
|
|
20
|
+
return 60.0 / self.bpm
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def whole_duration(self) -> int:
|
|
24
|
+
"""
|
|
25
|
+
计算音频矩阵总长
|
|
26
|
+
"""
|
|
27
|
+
return int(self.length * self.beat_duration * self.sample_rate)
|
|
28
|
+
|
chippymu/models.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
定义一些基础类型
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WaveType(str, Enum):
|
|
9
|
+
"""
|
|
10
|
+
几种波形
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
SINE = "sine"
|
|
14
|
+
SQUARE = "square"
|
|
15
|
+
TRIANGLE = "triangle"
|
|
16
|
+
SAWTOOTH = "sawtooth"
|
|
17
|
+
NOISE = "noise"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DrumType(str, Enum):
|
|
21
|
+
"""
|
|
22
|
+
几种鼓声
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
KICK = "kick"
|
|
26
|
+
SNARE = "snare"
|
|
27
|
+
HIHAT = "hihat"
|
chippymu/sound.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
后处理和音频播放
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sounddevice as sd # type: ignore
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from numpy import ndarray
|
|
9
|
+
|
|
10
|
+
from chippymu.configs import BasicParams
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def post_processing(channels: list[ndarray], volumes: list[float]) -> ndarray:
|
|
14
|
+
"""
|
|
15
|
+
后处理过程,包括音量控制、混音、裁剪、量化
|
|
16
|
+
"""
|
|
17
|
+
if len(channels) != len(volumes):
|
|
18
|
+
raise ValueError("channels和volumes长度不一致")
|
|
19
|
+
|
|
20
|
+
mix = np.zeros_like(channels[0])
|
|
21
|
+
for i in range(len(channels)):
|
|
22
|
+
mix += channels[i] * volumes[i]
|
|
23
|
+
|
|
24
|
+
mix = np.clip(mix, -1, 1)
|
|
25
|
+
|
|
26
|
+
mix_8bit = np.round(mix * 127).astype(np.int8)
|
|
27
|
+
|
|
28
|
+
return mix_8bit
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def play(data: ndarray, params: BasicParams):
|
|
32
|
+
sd.play(data=data, samplerate=params.sample_rate)
|
|
33
|
+
sd.wait()
|
|
34
|
+
|
chippymu/utils.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""
|
|
2
|
+
基础工具
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from chippymu.models import DrumType, WaveType
|
|
8
|
+
from numpy import ndarray
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def note_to_freq(*, note: int) -> float:
|
|
12
|
+
"""
|
|
13
|
+
将 MIDI 音符编号转换为频率 (Hz)
|
|
14
|
+
|
|
15
|
+
- 参数
|
|
16
|
+
- note: MIDI 音符编号
|
|
17
|
+
|
|
18
|
+
- 返回
|
|
19
|
+
- 频率 (Hz)
|
|
20
|
+
"""
|
|
21
|
+
return 440 * 2 ** ((note - 69) / 12)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def basic_wave_gen(
|
|
25
|
+
*,
|
|
26
|
+
wave_type: WaveType,
|
|
27
|
+
frequency: float,
|
|
28
|
+
duration: float,
|
|
29
|
+
amplitude: float = 1.0,
|
|
30
|
+
duty_cycle: float = 0.5,
|
|
31
|
+
sample_rate: int = 16000,
|
|
32
|
+
) -> ndarray:
|
|
33
|
+
"""
|
|
34
|
+
生成指定类型的波形数组。
|
|
35
|
+
|
|
36
|
+
- 参数:
|
|
37
|
+
- wave_type: 波形的类型,可以是 "sine"、"square"、"triangle"、"sawtooth" 或 "noise"。
|
|
38
|
+
- frequency: 波形的频率,单位为赫兹(Hz)。
|
|
39
|
+
- duration: 波形的持续时间,单位为秒(s)。
|
|
40
|
+
- amplitude: 波形的振幅,范围从 0 到 1。
|
|
41
|
+
- duty_cycle: Square 波形的占空比,范围从 0 到 1。
|
|
42
|
+
- sample_rate: 采样率,单位为赫兹(Hz),默认为16000,使用时可能从Params中获取。
|
|
43
|
+
|
|
44
|
+
- 返回:
|
|
45
|
+
- 一个包含波形数据的 NumPy 数组。
|
|
46
|
+
"""
|
|
47
|
+
t: ndarray = np.linspace(
|
|
48
|
+
start=0, stop=duration, num=int(sample_rate * duration), endpoint=False
|
|
49
|
+
)
|
|
50
|
+
phase: ndarray = (t * frequency) % 1
|
|
51
|
+
|
|
52
|
+
match wave_type:
|
|
53
|
+
case WaveType.SINE:
|
|
54
|
+
return amplitude * np.sin(2 * np.pi * frequency * t)
|
|
55
|
+
case WaveType.SQUARE:
|
|
56
|
+
return amplitude * np.where(phase < duty_cycle, 1, -1)
|
|
57
|
+
case WaveType.TRIANGLE:
|
|
58
|
+
return amplitude * np.where(phase < 0.5, -1 + 4 * phase, 3 - 4 * phase)
|
|
59
|
+
case WaveType.SAWTOOTH:
|
|
60
|
+
return amplitude * (2 * phase - 1)
|
|
61
|
+
case WaveType.NOISE:
|
|
62
|
+
return amplitude * (2 * np.random.rand(len(t)) - 1)
|
|
63
|
+
case _:
|
|
64
|
+
raise ValueError("未知波形类型")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def generate_kick(
|
|
68
|
+
*,
|
|
69
|
+
duration: float,
|
|
70
|
+
amplitude: float = 1.0,
|
|
71
|
+
sample_rate: int = 16000,
|
|
72
|
+
) -> ndarray:
|
|
73
|
+
"""
|
|
74
|
+
生成底鼓声音,低频正弦波带衰减。
|
|
75
|
+
|
|
76
|
+
- 参数:
|
|
77
|
+
- duration: 持续时间,单位秒
|
|
78
|
+
- frequency: 频率,单位赫兹
|
|
79
|
+
- amplitude: 振幅,范围0~1
|
|
80
|
+
- sample_rate: 采样率,默认16000
|
|
81
|
+
|
|
82
|
+
- 返回:
|
|
83
|
+
- 音频数据,ndarray
|
|
84
|
+
"""
|
|
85
|
+
frequency: int = 50
|
|
86
|
+
t: ndarray = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
|
|
87
|
+
envelope: ndarray = np.exp(-10 * t) # 衰减
|
|
88
|
+
return amplitude * envelope * np.sin(2 * np.pi * frequency * t)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def generate_snare(
|
|
92
|
+
*, duration: float, amplitude: float = 1.0, sample_rate: int = 16000
|
|
93
|
+
) -> ndarray:
|
|
94
|
+
"""
|
|
95
|
+
生成军鼓声音,噪声带衰减。
|
|
96
|
+
|
|
97
|
+
- 参数:
|
|
98
|
+
- duration: 持续时间,单位秒
|
|
99
|
+
- amplitude: 振幅,范围0~1
|
|
100
|
+
- sample_rate: 采样率,默认16000
|
|
101
|
+
|
|
102
|
+
- 返回:
|
|
103
|
+
- 音频数据,ndarray
|
|
104
|
+
"""
|
|
105
|
+
t: ndarray = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
|
|
106
|
+
noise: ndarray = 2 * np.random.rand(len(t)) - 1
|
|
107
|
+
envelope: ndarray = np.exp(-20 * t)
|
|
108
|
+
return amplitude * envelope * noise
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def generate_hihat(
|
|
112
|
+
*, duration: float, amplitude: float = 1.0, sample_rate: int = 16000
|
|
113
|
+
) -> ndarray:
|
|
114
|
+
"""
|
|
115
|
+
生成踩镲声音,短噪声带快速衰减。
|
|
116
|
+
|
|
117
|
+
- 参数:
|
|
118
|
+
- duration: 持续时间,单位秒
|
|
119
|
+
- amplitude: 音量
|
|
120
|
+
- sample_rate: 采样率,默认16000
|
|
121
|
+
|
|
122
|
+
- 返回:
|
|
123
|
+
- 噪声, ndarray
|
|
124
|
+
"""
|
|
125
|
+
t: ndarray = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
|
|
126
|
+
noise: ndarray = 2 * np.random.rand(len(t)) - 1
|
|
127
|
+
envelope: ndarray = np.exp(-50 * t)
|
|
128
|
+
return amplitude * envelope * noise
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def basic_drum_gen(
|
|
132
|
+
*,
|
|
133
|
+
drum_type: DrumType,
|
|
134
|
+
frequency: int = 50,
|
|
135
|
+
duration: float,
|
|
136
|
+
amplitude: float = 1.0,
|
|
137
|
+
sample_rate: int = 16000,
|
|
138
|
+
) -> ndarray:
|
|
139
|
+
"""
|
|
140
|
+
生成基本鼓声
|
|
141
|
+
|
|
142
|
+
- 参数:
|
|
143
|
+
- drum_type: 鼓声类型
|
|
144
|
+
- frequency: 频率,单位赫兹
|
|
145
|
+
- duration: 持续时间,单位秒
|
|
146
|
+
- amplitude: 振幅,范围0~1
|
|
147
|
+
- sample_rate: 采样率,单位赫兹
|
|
148
|
+
|
|
149
|
+
- 返回:
|
|
150
|
+
- 音频数据,ndarray
|
|
151
|
+
"""
|
|
152
|
+
match drum_type:
|
|
153
|
+
case DrumType.KICK:
|
|
154
|
+
return generate_kick(
|
|
155
|
+
duration=duration,
|
|
156
|
+
amplitude=amplitude,
|
|
157
|
+
sample_rate=sample_rate,
|
|
158
|
+
)
|
|
159
|
+
case DrumType.SNARE:
|
|
160
|
+
return generate_snare(
|
|
161
|
+
duration=duration, amplitude=amplitude, sample_rate=sample_rate
|
|
162
|
+
)
|
|
163
|
+
case DrumType.HIHAT:
|
|
164
|
+
return generate_hihat(
|
|
165
|
+
duration=duration, amplitude=amplitude, sample_rate=sample_rate
|
|
166
|
+
)
|
|
167
|
+
case _:
|
|
168
|
+
raise ValueError("未知鼓类型")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
chippymu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
chippymu/channelgen.py,sha256=56CdyMFMA9ToInqbU46hi6OIpJumOkBNS_nZWDp066I,2314
|
|
3
|
+
chippymu/configs.py,sha256=2lAIvQhW010vJxvnc_8WEgIbdsNe0dZjqQxSWXpM_0E,654
|
|
4
|
+
chippymu/models.py,sha256=3PUjKHONggFbm6A34Z6q_yR3jYn7CTl6HSvr6394ACU,352
|
|
5
|
+
chippymu/sound.py,sha256=sKvYZKxoL8G3hqmBvfqYmtW3qUoFjTh1X7uX_KyJn_k,764
|
|
6
|
+
chippymu/utils.py,sha256=QLk-32XpcnPfFwHocsIn5pXvKdizFcJUVhh7c6AZImc,4799
|
|
7
|
+
chippymu-0.1.0.dist-info/METADATA,sha256=Eg07ehGCHswGrOCLMHOoynxLDOnyUz8999xOwENjaSU,230
|
|
8
|
+
chippymu-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
chippymu-0.1.0.dist-info/entry_points.txt,sha256=Gg86qeAd6CivtSYQqWRqgS9tsClmZNEApTdGov6ssrg,43
|
|
10
|
+
chippymu-0.1.0.dist-info/RECORD,,
|