jack-organ.cpp (4002B)
1 #include "jack.hpp" 2 3 #include <jack/midiport.h> 4 5 #include <cassert> 6 7 #include <cmath> 8 9 #include <thread> 10 #include <chrono> 11 #include <iostream> 12 #include <iomanip> 13 14 using namespace std::literals::chrono_literals; 15 16 namespace jack { 17 18 } 19 20 jack::client client("h-synth", JackNoStartServer); 21 jack::port p1(client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); 22 jack::port p2(client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); 23 24 jack::ringbuffer midi_data(1024); 25 26 std::atomic_int midi_events; 27 std::atomic<jack_nframes_t> buffer_size, sample_rate; 28 29 int cb_buffer_size(jack_nframes_t nframes, void* arg) 30 { 31 buffer_size = nframes; 32 return 0; 33 } 34 int cb_sample_rate(jack_nframes_t nframes, void* arg) 35 { 36 sample_rate = nframes; 37 return 0; 38 } 39 40 static struct voice 41 { 42 float envelope, freq, sample; 43 unsigned short active : 1, velocity : 7, state : 3; 44 } voices[128]; 45 46 int cb_process(jack_nframes_t nframes, void* arg) 47 { 48 static char sustain; 49 void* buf = jack_port_get_buffer(*p1, nframes); 50 51 jack_midi_event_t event; 52 for (uint32_t i = 0; not jack_midi_event_get(&event, buf, i); ++i) 53 { 54 auto& voice = voices[event.buffer[1]]; 55 switch (event.buffer[0] >> 4 & 0x7) 56 { 57 case 0: // Note off 58 voice.active = 0; 59 continue; 60 case 1: // Note on 61 voice.active = 1; 62 voice.state = 4; 63 voice.velocity = event.buffer[2]; 64 continue; 65 case 3: // Controller change 66 switch (event.buffer[1]) 67 { 68 case 0x40: // Sustain 69 sustain = event.buffer[2] > 63; 70 continue; 71 } 72 /* FALLTHROUGH */ 73 default: 74 midi_data.write(event.buffer, event.size); 75 ++midi_events; 76 } 77 } 78 79 auto audio_buf = static_cast<float*>(jack_port_get_buffer(*p2, nframes)); 80 81 int srate = sample_rate; 82 83 for (unsigned i = 0; i < buffer_size; ++i) 84 { 85 float accum = 0; 86 for (auto& v : voices) 87 { 88 if (not v.state) 89 continue; 90 91 float pos = v.sample * 6.28318530718f / srate; 92 float sample = std::sin(pos / 2) + std::sin(pos) + std::sin(pos) + std::sin(pos * 2)*0.7f + std::sin(pos * 3)*.7f + std::sin(pos * 4)/2; 93 v.sample += v.freq; 94 if (v.sample >= srate * 6) 95 v.sample -= srate * 6; 96 97 float velocity = v.velocity / 127.f; 98 99 switch (v.state) 100 { 101 case 4: 102 v.envelope += velocity / 1000.f; 103 if (v.envelope >= velocity) 104 { 105 v.envelope = velocity; 106 v.state = 3; 107 } 108 break; 109 110 case 3: 111 v.envelope -= (v.envelope - velocity * 0.2f) * 0.00005f; 112 if (not (sustain || v.active)) 113 v.state = 2; 114 break; 115 116 case 2: 117 v.envelope *= 0.9998f; 118 if (v.envelope < 0.01f && sample < 0) 119 v.state = 1; 120 break; 121 122 case 1: 123 v.envelope *= 0.9998f; 124 if (sample >= 0) 125 { 126 v.envelope = 0; 127 v.sample = 0; 128 v.state = 0; 129 } 130 break; 131 } 132 accum += sample * v.envelope * .3; 133 } 134 audio_buf[i] = 2 / (1 + std::exp(accum)) - 1; 135 } 136 return 0; 137 } 138 139 void print_midi_events(void) 140 { 141 static int last = 0; 142 int current = midi_events; 143 144 if (current == last) 145 return; 146 147 last = current; 148 std::cout << "processed " << current << " MIDI events:\n" << std::hex; 149 150 for (unsigned char buf; midi_data.read(&buf, sizeof(buf)); /**/) 151 std::cout << std::setw(3) << +buf; 152 153 std::cout << std::dec << '\n'; 154 } 155 156 void print_buffer_size(void) 157 { 158 static int last = 0; 159 int current = buffer_size; 160 161 if (current == last) 162 return; 163 164 last = current; 165 std::cout << "Buffer size changed to: " << current << '\n'; 166 } 167 void print_sample_rate(void) 168 { 169 static int last = 0; 170 int current = sample_rate; 171 172 if (current == last) 173 return; 174 175 last = current; 176 std::cout << "Sample rate changed to: " << current << '\n'; 177 } 178 179 int main() 180 { 181 int note = 0; 182 for (auto& v : voices) 183 { 184 v.envelope = 0; 185 v.freq = 440 * std::pow(std::pow(2.f, 1.f/12), note++ - 69); 186 } 187 188 client.set_process_callback(cb_process, nullptr); 189 client.set_buffer_size_callback(cb_buffer_size, nullptr); 190 client.set_sample_rate_callback(cb_sample_rate, nullptr); 191 client.activate(); 192 193 for (;;) 194 { 195 std::this_thread::sleep_for(10ms); 196 print_buffer_size(); 197 print_sample_rate(); 198 print_midi_events(); 199 } 200 201 return 0; 202 }