回答編集履歴

5

日本語が下手すぎる点を訂正

2018/02/16 10:54

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -6,13 +6,13 @@
6
6
 
7
7
  - 最大振幅からいきなり振幅0にしても同じ
8
8
 
9
- - 同時音数が変化した際にampを突然変化させると波に連続性がなくなるため音割れする
10
-
11
- 同時音数というのは和音を再生する際には結構変化するので、本式のシンセサイザー(あるいはミキサー)には波形を調整するコンプレッサーとかリミッターと呼ばれるものが備わっていると思います。これはご質問のコードにあるように単に範囲外をclampするという方法では実現できません。そう方式では波形がひずむため結局音割れします。
12
-
13
-
14
-
15
- 波形を直接生成するような場合、こうした音の特性に注意する必要があると思います。自然界の音は[エンベロープ](https://www.g200kg.com/jp/docs/dic/envelope.html)に従って振幅が徐々に変化するのが普通です。MIDIシンセサイザーにはピンキリがあると思いますがどんな単純なものでも最小限のエンベロープ制御を行っており波形が不連続にならないように(音割れしないように)なっているとます
9
+ - 同時音数が変化した際にampを突然変化させると波に連続性がなくなるため音割れする
10
+
11
+ 同時音数というのは和音を再生する際には結構変化するので、本式のシンセサイザー(あるいはミキサー)には波形を調整するコンプレッサーとかリミッターと呼ばれるものが備わっています。これはご質問のコードにあるように単に範囲外をclampするという方法では実現できません。そうやってしま波形がひず結局音割れします。
12
+
13
+
14
+
15
+ 波形を直接生成するような場合、こうした音の特性に注意する必要があます。自然界の音は[エンベロープ](https://www.g200kg.com/jp/docs/dic/envelope.html)に従って振幅が徐々に変化するのが普通ですのソフトウェア波形を生成する場合でも最小限のエンベロープ制御を行波形が不連続にならないようにるとでしょう
16
16
 
17
17
 
18
18
 
@@ -44,7 +44,7 @@
44
44
 
45
45
 
46
46
 
47
- なお、前述したコンプレッサーとかリミッターをソフトウェア的に実現するのは少々面倒だと思います。振幅がオーバーフローしそうになったときだけ滑らかに波形を調整しないといけないのですが、**滑らかに調整**というのがなかなかめんどくさいのです。そこで、とりあえずは同時音数を適当な値に固定してしまい(例えば8とか)、演奏する際にその限界を超えて発音するような事態を避けるのが簡単と思います。同時音数というのは同時にNOTE_ONとなっている数の最大数よりも大きくなる点に注意してください。NOTE_OFFしてもリリースタイムの期間の間振幅を徐々に小さくしながら音が鳴り続けるため「音が完全に消えるまでには若干時間がかかる」からです。
47
+ なお、前述したコンプレッサーとかリミッターをソフトウェア的に実現するのは少々面倒だと思います。振幅がオーバーフローしそうになったときだけ滑らかに波形を調整しないといけないのですが、**滑らかに調整**というのがなかなかめんどくさいのです。そこで、とりあえずは同時音数を適当な値に固定してしまい(例えば8とか)、演奏する際にその限界を超えて発音するような事態を避けるのが簡単でしょう。同時音数というのは同時にNOTE_ONとなっている数の最大数よりも大きくなる点に注意してください。NOTE_OFFしてもリリースタイムの期間の間振幅を徐々に小さくしながら音が鳴り続けるため「音が完全に消えるまでには若干時間がかかる」からです。
48
48
 
49
49
 
50
50
 
@@ -54,7 +54,7 @@
54
54
 
55
55
 
56
56
 
57
- 下記コードでは0.4秒ごとにC4~B#4までの音を順に自動演奏していますが、その限りでは雑音はでないようでした。しかし・・・エンベロープを滑らかにしたつもりかつ同時音数10固定なんですが、自動演奏で和音にしたとたん演奏開始時点で雑音が・・・
57
+ 下記コードでは0.4秒ごとにC4~B#4までの音を順に自動演奏していますが、その限りでは雑音はでないようでした。しかし・・・エンベロープを滑らかにしたつもりかつ同時音数10固定なんですが、自動演奏で和音にしたとたん演奏開始時点で雑音が・・・
58
58
 
59
59
 
60
60
 

4

誤記訂正・コードコメント訂正

2018/02/16 10:54

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -44,7 +44,7 @@
44
44
 
45
45
 
46
46
 
47
- なお、前述したコンプレッサーとかリミッターをソフトウェア的に実現するのは少々面倒だと思います。振幅がオーバーフローしそうになったときだけ滑らかに波形を調整しないといけないのですが、**滑らかに調整**というのがなかなかめんどくさいのです。そこで、とりあえずは同時撥音数を適当な値に固定してしまい(例えば8とか)、演奏する際にその限界を超えて発音するような事態を避けるのが簡単と思います。同時撥音数というのは同時にNOTE_ONとなっている数の最大数よりも大きくなる点に注意してください。NOTE_OFFしてもリリースタイムの期間の間神父が徐々に小さくなるため「音が完全に消えるまでには若干時間がかかる」からです。
47
+ なお、前述したコンプレッサーとかリミッターをソフトウェア的に実現するのは少々面倒だと思います。振幅がオーバーフローしそうになったときだけ滑らかに波形を調整しないといけないのですが、**滑らかに調整**というのがなかなかめんどくさいのです。そこで、とりあえずは同時撥音数を適当な値に固定してしまい(例えば8とか)、演奏する際にその限界を超えて発音するような事態を避けるのが簡単と思います。同時撥音数というのは同時にNOTE_ONとなっている数の最大数よりも大きくなる点に注意してください。NOTE_OFFしてもリリースタイムの期間の間振幅を徐々に小さくがら音が鳴り続けるため「音が完全に消えるまでには若干時間がかかる」からです。
48
48
 
49
49
 
50
50
 
@@ -138,7 +138,7 @@
138
138
 
139
139
  # 以下のように、3度の和音にしたら同時発音数=10でも音割れした。(Win10 64bit)
140
140
 
141
- # なぜそうなるか分かりませんでした... ><
141
+ # =>PyCharmのデバッグ実行は音割れしたが通常実行にすると音割れしなくなった
142
142
 
143
143
  # ---------------------------------------------
144
144
 

3

追記2

2018/02/16 10:49

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -62,6 +62,12 @@
62
62
 
63
63
 
64
64
 
65
+ 追記2:PyCharm上で実行していたのですが、ふと思いついて「デバッグ実行」ではなく普通の「実行」にしたら和音にしても雑音でなくなりました!
66
+
67
+ 想像ですが、PyCharmでデバッグ実行していたためインタープリタの実行スピードが音声再生においついてなかったのだと思います・・・
68
+
69
+
70
+
65
71
  ```Python
66
72
 
67
73
  import math

2

コード追記

2018/02/16 09:44

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -45,3 +45,355 @@
45
45
 
46
46
 
47
47
  なお、前述したコンプレッサーとかリミッターをソフトウェア的に実現するのは少々面倒だと思います。振幅がオーバーフローしそうになったときだけ滑らかに波形を調整しないといけないのですが、**滑らかに調整**というのがなかなかめんどくさいのです。そこで、とりあえずは同時撥音数を適当な値に固定してしまい(例えば8とか)、演奏する際にその限界を超えて発音するような事態を避けるのが簡単と思います。同時撥音数というのは同時にNOTE_ONとなっている数の最大数よりも大きくなる点に注意してください。NOTE_OFFしてもリリースタイムの期間の間神父が徐々に小さくなるため「音が完全に消えるまでには若干時間がかかる」からです。
48
+
49
+
50
+
51
+ ---
52
+
53
+ 追記:実際に単純なエンベロープにしてやってみた例を挙げてみます。少々長いのでちょっと気が引けますが。自分の環境ではMIDI-INがすぐに使えないので、Key_inputの部分は自動演奏に置き換えています。質問者さんの環境ではこの関数を書き替えずに実行すれば演奏の通りに音が鳴らせると思います。(すみませんが、自動演奏に変えたため元のコードからpygame.midiの部分のコードは削除してあります)
54
+
55
+
56
+
57
+ 下記コードでは0.4秒ごとにC4~B#4までの音を順に自動演奏していますが、その限りでは雑音はでないようでした。しかし・・・エンベロープを滑らかにしたつもりかつ同時撥音数10固定なんですが、自動演奏で和音にしたとたん演奏開始時点で雑音が・・・
58
+
59
+
60
+
61
+ 単なるバグなのかも知れませんし、Pythonのスピードとか自分の配慮が不足している点が何かあるのかも知れません・・・
62
+
63
+
64
+
65
+ ```Python
66
+
67
+ import math
68
+
69
+ import pyaudio
70
+
71
+ import struct
72
+
73
+ import time
74
+
75
+
76
+
77
+ # PyAudio制御関連=============================
78
+
79
+ SAMPLE_RATE = 44100
80
+
81
+
82
+
83
+ if __name__ == '__main__':
84
+
85
+ p = pyaudio.PyAudio()
86
+
87
+ # ストリームを開く
88
+
89
+ stream = p.open(format=pyaudio.paInt16, channels=1, rate=SAMPLE_RATE, output=1, stream_callback=None)
90
+
91
+
92
+
93
+
94
+
95
+ def End(): # 終了処理(必ず入れる)
96
+
97
+ stream.close()
98
+
99
+ p.terminate()
100
+
101
+
102
+
103
+
104
+
105
+ # Key管理================================
106
+
107
+
108
+
109
+ key_list = [] # 押されてるキーを保持するリスト
110
+
111
+ start_time = time.time() # MIDI-INなしで自動演奏するための小細工
112
+
113
+
114
+
115
+
116
+
117
+ def Key_input():
118
+
119
+ # MIDI-INなしで自動演奏するための小細工
120
+
121
+ # MIDI-INが使えるならオリジナルのコードそのままでよいはず・・・
122
+
123
+ global key_list
124
+
125
+ t = (time.time() - start_time)
126
+
127
+ if 3 < t < 20:
128
+
129
+ key = 60 + int((t - 3) / 0.4) % 12
130
+
131
+ key_list = [[key, 110]]
132
+
133
+ # 以下のように、3度の和音にしたら同時発音数=10でも音割れした。(Win10 64bit)
134
+
135
+ # なぜそうなるか分かりませんでした... ><
136
+
137
+ # ---------------------------------------------
138
+
139
+ # key_list = [[key, 120], [key + 4, 100]]
140
+
141
+ else:
142
+
143
+ key_list = []
144
+
145
+ return
146
+
147
+
148
+
149
+
150
+
151
+ # 周波数とか作るところ=======================
152
+
153
+
154
+
155
+
156
+
157
+ class Oscillator:
158
+
159
+ def __init__(self, key, max_level, sustain_level, attack_time, decay_time, release_time):
160
+
161
+ self.key = key
162
+
163
+ freq = 440.0 * (2 ** ((key - 69) / 12))
164
+
165
+ self.dt = math.pi * 2 * freq / SAMPLE_RATE
166
+
167
+ self.max = max_level
168
+
169
+ self.sustain = sustain_level
170
+
171
+ self.attack_t = int(attack_time * SAMPLE_RATE)
172
+
173
+ self.decay_t = int(decay_time * SAMPLE_RATE)
174
+
175
+ self.release_t = int(release_time * SAMPLE_RATE)
176
+
177
+ self.released = None
178
+
179
+ self.time = 0
180
+
181
+ self.amp = 0
182
+
183
+
184
+
185
+ def calc_amp(self):
186
+
187
+ if self.released is None:
188
+
189
+ # まだNOTE_ON状態
190
+
191
+ if self.time < self.attack_t:
192
+
193
+ return Oscillator.interpolate(self.time, self.attack_t, 0.0, self.max)
194
+
195
+ else:
196
+
197
+ return Oscillator.interpolate(self.time - self.attack_t, self.decay_t, self.max, self.sustain)
198
+
199
+ else:
200
+
201
+ # NOTE_OFF状態
202
+
203
+ return Oscillator.interpolate(self.time - self.released, self.release_t, self.sustain, 0.0)
204
+
205
+
206
+
207
+ # 振幅の直線補間用
208
+
209
+ @staticmethod
210
+
211
+ def interpolate(cur_t, max_t, start_level, end_level):
212
+
213
+ t = cur_t / max_t if cur_t < max_t else 1
214
+
215
+ return start_level * (1 - t) + end_level * t
216
+
217
+
218
+
219
+ def wave(self):
220
+
221
+ self.amp = self.calc_amp()
222
+
223
+ w = self.amp * math.sin(self.time * self.dt)
224
+
225
+ self.time += 1
226
+
227
+ return w
228
+
229
+
230
+
231
+ def note_off(self):
232
+
233
+ if self.released is None:
234
+
235
+ self.sustain = self.amp # attack/decay中にNOTE_OFFされた場合への配慮
236
+
237
+ self.released = self.time
238
+
239
+ return True
240
+
241
+ else:
242
+
243
+ return False
244
+
245
+
246
+
247
+ def is_playing(self):
248
+
249
+ return self.released is None or self.time < self.released + self.release_t
250
+
251
+
252
+
253
+
254
+
255
+ class Synthesizer:
256
+
257
+ def __init__(self, polyphony=8):
258
+
259
+ self.oscillators = {}
260
+
261
+ self.polyphony = polyphony
262
+
263
+ buffer_size = 1048 // 2 # 44100 1sec 4410 0.1sec 441 0.01sec
264
+
265
+ latency = buffer_size / SAMPLE_RATE
266
+
267
+ self.wave = [0] * int(latency * SAMPLE_RATE)
268
+
269
+ self.pack_format = 'h' * len(self.wave)
270
+
271
+ print("polyphony=", self.polyphony, "latency=", latency)
272
+
273
+
274
+
275
+ def generate_wave(self, keys):
276
+
277
+ """実際の波形を生成"""
278
+
279
+ self.update_oscillators(keys)
280
+
281
+ wave = self.wave
282
+
283
+ # print(keys)
284
+
285
+ amp = 32767 / self.polyphony # 同時発音数を固定とする
286
+
287
+ for i in range(len(self.wave)):
288
+
289
+ s = int(amp * sum(map(Oscillator.wave, self.oscillators.values())))
290
+
291
+ wave[i] = s # max(-32767, min(s, 32767)) # clampしない
292
+
293
+ wave = struct.pack(self.pack_format, *wave)
294
+
295
+ return wave
296
+
297
+
298
+
299
+ def update_oscillators(self, keys):
300
+
301
+ """NOTE_ONのキー集合によりオシレーター群を更新する"""
302
+
303
+
304
+
305
+ # もはや発音しないオシレーターを削除
306
+
307
+ osc_exists = len(self.oscillators) > 0
308
+
309
+ self.oscillators = {osc.key: osc for osc in filter(Oscillator.is_playing, self.oscillators.values())}
310
+
311
+ if osc_exists and len(self.oscillators) == 0:
312
+
313
+ print("all oscillators removed")
314
+
315
+
316
+
317
+ # 新たにNOTE_ONとなったオシレーターを追加
318
+
319
+ new_keys = filter(lambda key: key not in self.oscillators, keys)
320
+
321
+ for key in new_keys:
322
+
323
+ max_level = keys[key] / 127
324
+
325
+ sustain_level = max_level * 0.9
326
+
327
+ attack_time = 0.3
328
+
329
+ decay_time = 0.1
330
+
331
+ release_time = 0.1
332
+
333
+ self.oscillators[key] = Oscillator(key, max_level, sustain_level, attack_time, decay_time, release_time)
334
+
335
+ print("note on", key)
336
+
337
+
338
+
339
+ # NOTE_OFFとなったオシレーターの状態を変更(リリース制御のため)
340
+
341
+ for osc_key in self.oscillators:
342
+
343
+ if osc_key not in keys:
344
+
345
+ if self.oscillators[osc_key].note_off():
346
+
347
+ print("note off", osc_key)
348
+
349
+
350
+
351
+ # 再生================================
352
+
353
+
354
+
355
+
356
+
357
+ def play(stream, data): # 再生用関数、ストリームと波形データを引数に
358
+
359
+ # チャンク単位でストリームに出力し音声を再生
360
+
361
+ chunk = 1048
362
+
363
+ sp = 0 # 再生位置ポインタ
364
+
365
+ buffer = data[sp:sp+chunk]
366
+
367
+ while buffer:
368
+
369
+ stream.write(buffer)
370
+
371
+ sp = sp + chunk
372
+
373
+ buffer = data[sp:sp+chunk]
374
+
375
+
376
+
377
+ # ==========================================
378
+
379
+
380
+
381
+
382
+
383
+ if __name__ == '__main__':
384
+
385
+ synthesiser = Synthesizer(10)
386
+
387
+ while True:
388
+
389
+ Key_input() # キーの取得
390
+
391
+ keys = {k[0]: k[1] for k in key_list}
392
+
393
+ wave = synthesiser.generate_wave(keys)
394
+
395
+ play(stream, wave) # 波形の再生
396
+
397
+ End() # このままでは決して呼び出されませんが...
398
+
399
+ ```

1

文章不備訂正

2018/02/16 09:33

投稿

KSwordOfHaste
KSwordOfHaste

スコア18394

test CHANGED
@@ -12,11 +12,11 @@
12
12
 
13
13
 
14
14
 
15
- こうした音の特性に注意する必要があるのではなでしょうか。自然界の音は[エンベロープ](https://www.g200kg.com/jp/docs/dic/envelope.html)に従って振幅が徐々に変化するのが普通です。MIDIのシンセサイザーにはピンキリがあると思いますが、どんな単純なものでも最小限のエンベロープの配慮により波形が不連続になることよる音割れを避けるようになっていると思います。
15
+ 波形を直接生成するような場合、こうした音の特性に注意する必要があると思ます。自然界の音は[エンベロープ](https://www.g200kg.com/jp/docs/dic/envelope.html)に従って振幅が徐々に変化するのが普通です。MIDIのシンセサイザーにはピンキリがあると思いますが、どんな単純なものでも最小限のエンベロープ制御を行っており波形が不連続にならないよう音割れしないようになっていると思います。
16
16
 
17
17
 
18
18
 
19
- 基本体なエンベロープのしかたとしては以下のような感じに考えるとよいと思います。
19
+ なお、基本体なエンベロープ制御は以下のような感じに考えるとよいと思います。
20
20
 
21
21
 
22
22