jack-tools

A handful of JACK audio tools
git clone git://henryandlizzy.uk/jack-tools
Log | Files | Refs

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 }