加算合成シンセに関するソースコードのメモ。
シンセに関する情報がコンパクトにまとまっていていい資料だったので、
保守の意味合いで別途残しておきます。
言語はGLSLです。
ベースになるプレゼン資料はこれ。
additive_slides.pdf (graffathon.fi)
#define RATE (44100.0) #define NOTEMAGIC (1.059460646483) #define TAU (2.0*PI) #define NOTEFREQ(pitch) (pow(NOTEMAGIC, pitch) * 440.0) #define PI (3.14159265358979323846) #define FBO_WIDTH (4096.0) #define EVENT_MAX (64) #define SHRT_MAX 32767 #define BUF_WIDTH 1024 uniform int framesRendered; #ifdef VERTEX in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } #endif #ifdef FRAGMENT //layout(location=0) out vec4 out_Color; layout(location=0) out vec4 out_Value; float saw(float t) { return fract(t)*2.0-1.0; } float softsaw(float t, float s) { return mix(fract(t)*2-1, -sin(t*2.0*PI), s); } // Range: [-1, 1] float rand(float t) { return sin(1e5*sin(t*100.)); } float phatsaw(float t, float form) { float w = 0.0; const int COUNT = 5; for (int i=0;i<COUNT;i++) { w+= softsaw(t + sin(0.4*i)*0.2, form) * (1.0/COUNT); } return w; } float arp_pattern(float t) { //int[](0,4,9,7,2,4,7,9); int tones[16] = int[](0,4,9,7,2,4,7,9, -1,4,9,7,-5,4,7,9); float arp = 0.0; float at = t; int arpind = int(at*4) % 16; float arpnote = tones[arpind] + 0.31; arp = pow(phatsaw(t*NOTEFREQ(arpnote), 0.7 + sin(t*.2)*0.1), 3.0); arp *= 0.7*(0.4 + sin(arpind*3.0)*0.1)*max(0.4, -softsaw(at*6.0, 0.8)); return arp; } vec2 smallsyn(float t, float f) { float basstime = t/8.0; return 0.5* ((1.0-fract(basstime*8.0)) * (fract(basstime*12.0)) * abs(sin(basstime*9.0*PI))) *vec2(softsaw(t*f*12., 0.7+.3*fract(basstime*2.0))); } vec2 percussion(float t) { float b = (t-0.20)*0.5; //float perc = 0.07*sin(b*2*PI*0.5) * clamp((sin(b*2*PI*8)-0.55)*40.0, 0.0, 1.0) * rand(t); float perc = 0.04*sin(b*2*PI*0.5) * clamp((sin(b*2*PI*8)-(1.8-t*0.013))*40.0, 0.0, 1.0) * rand(t) * clamp((t-32)*0.018, 0.0, 1.0) ; float tt = 20+mod(t, 30.); perc += 0.6 * clamp((-0.8-softsaw((b+0.6)*1.0, 0.05)), 0.0, 1.0) * sin(tt*tt*tt*1e4); return vec2(perc); } float bd(float t) { float tt = (1.0-min(1.0, t*0.6)); tt *= tt * tt; float amp = 1.0; amp = clamp(tt - 0.3, 0.0, 1.0); float f = 40.0 + tt*50.0; float w = sin(2.0*PI*t*f); return w*0.5 *amp; } // thanks iq float impulse(float k, float x) { float h = k*x; return h*exp(1.0-h); } vec2 synth(float t, out vec2 out_bass) { float volume = 0.4; vec2 wave = vec2(0.0f);//sin(t*2.0*PI*(1000.0) + chn) * volume; int count = 150; float mult = t*2.0*PI; float vol = 1.0/count; //int tones[8] = int[](0,2,4,5,7,9,10,3); int tones[8] = int[](0,4,9,7,2,4,7,9); int bass_range = 2; if (t > 32) { bass_range = 4; } float basstime = t/8.0; int bass_index = (int(basstime) % bass_range); float bassfreq = NOTEFREQ(1.0*tones[bass_index] - 12*3 + 0.2); //float bassenv = min(1., pow(t*0.025, 2.0)) * max(0.6, pow(1. - fract(basstime*1.0), 1.0)); float bassenv = min(1., pow(t*0.025, 2.0)) * min(fract(basstime)*150.0, 1.0) * min((1.0-fract(basstime))*100.0, 1.0) * max(0.6, pow(1. - fract(basstime*1.0), 1.0)); out_bass = vec2(0.3 * bassenv * softsaw(t*bassfreq, max(0.9, 1.0-t*0.0025))); float soft = (1.0 + sin(t*0.1))*0.5; for (int i=0;i<count;i++) { //int add = int(t*4.0) % 5; //int add = int(t/2) % 4; //float amp = clamp((sin(t*PI*9.0 + i*20.0)-0.5)*(10.+sin(1.*i)*9.), 0.0, 1.0); float amp = 3.0; //float amp = 3.0 * max(0.3, fract(t*2.0 - i*0.001)); //float amp = clamp((1.0-saw(i*1.0)), 0.0, 1.0); //float amp = 0.5f; int index = (i) % 8; int oct = (i/12) % 4; float note = -12*2 + (tones[index] + oct*12) % (12*6) + sin(0.001*i)*3.0; float hz = NOTEFREQ(note); float p = 0.5 + sin(i*1.)*0.2; // ::: STEREO SOUND ::: wave += vec2(softsaw(t*hz, soft))*vol*amp*vec2(p, 1.0-p); } //wave *= max(0.3, fract(t*2.0)); vec2 perc = vec2(1.0, 1.0) * percussion(t) + vec2(0.2, 0.3) * percussion(t-0.1) + vec2(0.2, 0.1) * percussion(t-0.18) + vec2(0.1, 0.2) * percussion(t-0.22); wave *= clamp(t, 0.0, 1.0); float synth_fadeout = clamp(1.0-(t-115)*0.04, 0.0, 1.0); wave *= max(0.5, synth_fadeout); // fade out out_bass *= synth_fadeout; perc *= clamp((t-32)*0.08, 0.0, 1.0); // start fade in at 32s perc *= clamp(1.0-(t-110)*0.05, 0.0, 1.0); // fade out vec2 audio; audio = wave + perc; vec2 arp = vec2(arp_pattern(t), arp_pattern(t-0.03)*0.8); arp += vec2(arp_pattern(t-0.3)*0.3, arp_pattern(t-0.6)*0.2); arp *= clamp((t-60)*0.1, 0.0, 1.0); vec2 syn = vec2(0.7, 1.0) * smallsyn(t, bassfreq) + vec2(0.6, 0.3) * smallsyn(t-0.05, bassfreq-0.1); syn += 0.1*smallsyn(t*4.0, bassfreq); syn *= clamp((t-122)*0.1, 0.0, 0.4); audio += syn; audio += arp; out_bass += vec2(bd(fract(t))) * float[](1., 0., 0., 0.5)[int(floor(t))%4] * clamp(1.0-(t-120)*0.1, 0.0, 1.0) * clamp((t-80)*0.1, 0.0, 1.0); float fadeout = clamp(1.0-(t-125)*0.035, 0.0, 1.0); audio *= fadeout; // Noise mat peak. audio += vec2(rand(t*.9), rand(t*.8)) * 0.04 * impulse(0.4, sqrt(10.0*max(0.0, t-96.0))); return audio; } void main(void) { int pos = framesRendered + (int(gl_FragCoord.x)) + (int(gl_FragCoord.y))*BUF_WIDTH; int tpos = pos/2; float t = float(pos)/RATE; t*=0.93; // for vaporwave //t += sin(t)*0.01; // LOL detune //int chan = pos % 2; vec2 bass; vec2 wave = synth(t, bass); vec2 dummy; vec2 reverb = vec2(0.0f); const int REV = 3; const float REV_LENGTH = 0.1f; for (int i=1;i<REV;i++) { reverb += synth(t - i*(REV_LENGTH/REV), dummy) * 0.03*(REV/i); } wave += bass; wave += vec2(-reverb.x, reverb.y); //wave = clamp(sign(wave)*sqrt(wave * sign(wave) * 1.4), -0.99, 0.99); // clamping not strictly necessary wave = clamp(wave * 1.4, -0.99, 0.99); // clamping not strictly necessary int left = int((wave.x)*SHRT_MAX); int right = int((wave.y)*SHRT_MAX); // STEREO SOUND /* if (chan == 1) { left = right; }*/ out_Value = vec4(float(left & 0xFF)/256.0, ((left>>8) & 0xFF)/256.0, float(right & 0xFF)/256.0, ((right>>8) & 0xFF)/256.0); } #endif