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 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,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: chippymu
3
+ Version: 0.1.0
4
+ Summary: 一个用numpy实现的8bit音乐生成器
5
+ Author-email: wolido <270262953@qq.com>
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: numpy>=2.3.0
8
+ Requires-Dist: sounddevice>=0.5.2
@@ -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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ chippymu = chippymu:main