1 2 /** 3 * A simple FM synthesizer with Jack. 4 * It is a port of the miniFMsynth example from ALSA. 5 */ 6 7 module miniFMsynth; 8 import jack.client; 9 import jack.midiport; 10 import std.stdio; 11 import std.math; 12 import core.stdc.string; 13 14 void main() 15 { 16 JackClient client = new JackClient; 17 client.open("miniFMsynth", JackOptions.JackNoStartServer, null); 18 scope(exit) client.close(); 19 20 writeln("New jack_client with name: " ~ client.get_name()); 21 22 JackPort midi = client.register_port("In", JACK_DEFAULT_MIDI_TYPE, JackPortFlags.JackPortIsInput, 0); 23 JackPort out1 = client.register_port("Out1", JACK_DEFAULT_AUDIO_TYPE, JackPortFlags.JackPortIsOutput, 0); 24 JackPort out2 = client.register_port("Out2", JACK_DEFAULT_AUDIO_TYPE, JackPortFlags.JackPortIsOutput, 0); 25 26 auto fm = new SynthFM; 27 fm.srate = client.get_sample_rate(); 28 fm.buf = new short[client.get_buffer_size()]; 29 30 fm.modulation = 7.8; 31 fm.harmonic = 3; 32 fm.subharmonic = 5; 33 fm.transpose = 24; 34 fm.attack = 0.01; 35 fm.decay = 0.8; 36 fm.sustain = 0.0; 37 fm.release = 0.1; 38 fm.pitch = 0.0; 39 40 client.process_callback = delegate int(jack_nframes_t nframes) { 41 JackMidiPortBuffer midibuf = midi.get_midi_buffer(nframes); 42 foreach (JackMidiEvent event; midibuf.iter_events()) { 43 if (event.size == 3) { 44 if (event.buffer[0] == 0x80 || 45 event.buffer[0] == 0x90 && event.buffer[2] == 0) 46 fm.noteoff(event.buffer[1], event.buffer[2]); 47 else if (event.buffer[0] == 0x90) 48 fm.noteon(event.buffer[1], event.buffer[2]); 49 } 50 } 51 52 float *buf1 = out1.get_audio_buffer(nframes); 53 float *buf2 = out2.get_audio_buffer(nframes); 54 55 fm.compute(nframes); 56 57 for (jack_nframes_t i = 0; i < nframes; ++i) { 58 buf1[i] = cast(double)fm.buf[i] / short.max; 59 } 60 buf2[0..nframes] = buf1[0..nframes]; 61 62 return 0; 63 }; 64 65 client.activate(); 66 67 writeln("Press a key to stop."); 68 stdin.readln(); 69 } 70 71 class SynthFM 72 { 73 double srate; 74 75 enum POLY = 10; 76 enum GAIN = 5000.0; 77 78 short[] buf; 79 double pitch, modulation, attack, decay, sustain, release; 80 double[POLY] phi, phi_mod, velocity, env_time, env_level; 81 int harmonic, subharmonic, transpose, rate; 82 int[POLY] note, gate, note_active; 83 84 this() { 85 for (uint i = 0; i < POLY; ++i) { 86 phi[i] = 0.0; 87 phi_mod[i] = 0.0; 88 velocity[i] = 0.0; 89 env_time[i] = 0.0; 90 env_level[i] = 0.0; 91 } 92 } 93 94 void noteon(int note, int vel) 95 { 96 for (uint l1 = 0; l1 < POLY; l1++) { 97 if (! note_active[l1]) { 98 this.note[l1] = note; 99 velocity[l1] = vel / 127.0; 100 env_time[l1] = 0; 101 gate[l1] = 1; 102 note_active[l1] = 1; 103 break; 104 } 105 } 106 } 107 108 void noteoff(int note, int vel) 109 { 110 for (uint l1 = 0; l1 < POLY; l1++) { 111 if (gate[l1] && note_active[l1] && (this.note[l1] == note)) { 112 env_time[l1] = 0; 113 gate[l1] = 0; 114 } 115 } 116 } 117 118 static double envelope(int *note_active, int gate, double *env_level, double t, double attack, double decay, double sustain, double release) 119 { 120 if (gate) { 121 if (t > attack + decay) return(*env_level = sustain); 122 if (t > attack) return(*env_level = 1.0 - (1.0 - sustain) * (t - attack) / decay); 123 return(*env_level = t / attack); 124 } else { 125 if (t > release) { 126 if (note_active) *note_active = 0; 127 return(*env_level = 0); 128 } 129 return(*env_level * (1.0 - t / release)); 130 } 131 } 132 133 void compute(uint nframes) 134 { 135 short *buf = this.buf.ptr; 136 137 memset(buf, 0, nframes * float.sizeof); 138 for (int l2 = 0; l2 < POLY; l2++) { 139 if (note_active[l2]) { 140 double f1 = 8.176 * exp(cast(double)(transpose+note[l2]-2)*log(2.0)/12.0); 141 double f2 = 8.176 * exp(cast(double)(transpose+note[l2])*log(2.0)/12.0); 142 double f3 = 8.176 * exp(cast(double)(transpose+note[l2]+2)*log(2.0)/12.0); 143 double freq_note = (pitch > 0) ? f2 + (f3-f2)*pitch : f2 + (f2-f1)*pitch; 144 double dphi = PI * freq_note / (srate / 2.0); 145 double dphi_mod = dphi * cast(double)harmonic / cast(double)subharmonic; 146 for (int l1 = 0; l1 < nframes; l1++) { 147 phi[l2] += dphi; 148 phi_mod[l2] += dphi_mod; 149 if (phi[l2] > 2.0 * PI) phi[l2] -= 2.0 * PI; 150 if (phi_mod[l2] > 2.0 * PI) phi_mod[l2] -= 2.0 * PI; 151 double env = envelope(¬e_active[l2], gate[l2], &env_level[l2], env_time[l2], attack, decay, sustain, release); 152 double sound = GAIN * env 153 * velocity[l2] * sin(phi[l2] + modulation * sin(phi_mod[l2])); 154 env_time[l2] += 1.0 / srate; 155 buf[l1] += sound; 156 } 157 } 158 } 159 } 160 }