shaderシンセ用の自作クラスを使ってみる。その3
キーボードを鍵盤に見立てて、音階を出せるようにしました。ADSRをツマミで調整できるようにしました。
ブログに書けるスクリプトは、このくらいかな。これ以上は、音色の調整の世界、個人の好みの問題になるので、書いてもスルーのみたい。
一回、ブログとかに露出すると、考えが整理されます。だいぶ、スッキリしました。あとは、地味に遊んでいきます。
glSynth.pyはここ
from glSynth import * from PySide2.QtGui import QFont from PySide2.QtWidgets import QFrame class SynthDial(QFrame): def __init__(self, name, min, max, init=0): super().__init__() self.setFrameShape(QFrame.Panel) self.setLineWidth(2) self.layout = QVBoxLayout(self) self.value = 0 self.min = min self.max = max font = QFont() font.setBold(True) font.setWeight(75) label = QLabel(name) label.setFont(font) self.layout.addWidget(label) self.dial = QDial() self.layout.addWidget(self.dial) self.valuelabel = QLabel() self.valuelabel.setFont(font) self.layout.addWidget(self.valuelabel) self.dial.setSliderPosition(init) self.valueChanged(init) self.dial.valueChanged.connect(self.valueChanged) def valueChanged(self, v): self.value = (self.max - self.min) * v / 99 + self.min self.valuelabel.setText(format(self.value,'.1f')) class MainWindow(GLSynth): def __init__(self, parent=None): super().__init__() self.note = 69 self.setVolume(0.8) def shaderScript(self): return ''' uniform int note; uniform vec4 env; #define OSC(f) saw(f) float saw(float f) { return 1.-fract(f)*2.; } float square(float f) { return sign(fract(f)-0.5); } float note2freq(int n) { return 440.0*exp2((float(n)-69.0)/12.0); } float adsr(float t, vec4 e, float s) { return max(0.0, min(1.0, t/max(0.0001, e.x)) - min((1.0 - s) ,max(0.0, t - e.x)*(1.0 - s)/max(0.0001, e.y)) - max(0.0, t - e.z)*s/max(0.0001, e.w)); } void main(){ float freq = note2freq(note); gain = vec2( OSC(freq*iTime) * adsr(iTime, env, iGateTime)); } ''' def initializeWindow(self): self.attack = SynthDial("attack", 0, 0.5, 0); self.addWidget(self.attack); self.decay = SynthDial("decay", 0, 1, 30); self.addWidget(self.decay); self.sustain = SynthDial("sustain", 0, 1, 50); self.addWidget(self.sustain); self.release = SynthDial("release", 0, 1, 30); self.addWidget(self.release); def setUniform(self): glUniform1i(glGetUniformLocation(self.program, "note"), self.note) glUniform4f(glGetUniformLocation(self.program, "env"), self.attack.value, self.decay.value, self.sustain.value, self.release.value) def keyPressEvent(self, e): super().keyPressEvent(e) self.phase = 1<<32 self.gate = 0 note = { Qt.Key_A: 59, Qt.Key_S: 60, Qt.Key_E: 61, Qt.Key_D: 62, Qt.Key_R: 63, Qt.Key_F: 64, Qt.Key_G: 65, Qt.Key_Y: 66, Qt.Key_H: 67, Qt.Key_U: 68, Qt.Key_J: 69, Qt.Key_I: 70, Qt.Key_K: 71, Qt.Key_L: 72, } if e.key() in note.keys(): self.note = note[e.key()] self.phase = 0 self.gate = 1<<32 if __name__ == '__main__': import sys app = QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_())