1 
2 /**
3  * A MIDI processor for Jack.
4  * It alters the velocity of note messages according to a given curve function.
5  */
6 
7 module velocityCurve;
8 import jack.client;
9 import jack.midiport;
10 import std.stdio;
11 import std.math;
12 import core.stdc.string;
13 
14 /// the curve function [0,1]->[0,1] is defined as
15 /// 1-((exp(s*(1-x))-1)/(exp(s)-1))
16 /// with the constant slope factor s=4
17 double curve(double x) {
18   enum curve_s = 4.0;
19   enum exp_curve_s = /*exp(curve_s)*/ 54.598150033144236;
20   return 1.0 - (fastexp(curve_s * (1.0 - x)) - 1.0) / (exp_curve_s - 1.0);
21 }
22 
23 void main()
24 {
25   JackClient client = new JackClient;
26   client.open("velocityCurve", JackOptions.JackNoStartServer, null);
27   scope(exit) client.close();
28 
29   writeln("New jack_client with name: " ~ client.get_name());
30 
31   JackPort midi_in = client.register_port("In", JACK_DEFAULT_MIDI_TYPE, JackPortFlags.JackPortIsInput, 0);
32   JackPort midi_out = client.register_port("Out", JACK_DEFAULT_MIDI_TYPE, JackPortFlags.JackPortIsOutput, 0);
33 
34   client.process_callback = delegate int(jack_nframes_t nframes) {
35     JackMidiPortBuffer inbuf = midi_in.get_midi_buffer(nframes);
36     JackMidiPortBuffer outbuf = midi_out.get_midi_buffer(nframes);
37 
38     outbuf.clear();
39 
40     foreach (JackMidiEvent event; inbuf.iter_events()) {
41 
42       if (event.buffer[0] & 0x80 || event.buffer[0] & 0x90) {
43         double vel = event.buffer[2] / 127.0;
44         double newvel = curve(vel);
45         if (newvel < 0.0) newvel = 0.0;
46         else if (newvel > 1.0) newvel = 1.0;
47         event.buffer[2] = cast(ubyte)lrint(newvel * 127.0);
48       }
49 
50       outbuf.write_event(event.time, event.buffer, event.size);
51     }
52 
53     return 0;
54   };
55 
56   client.activate();
57 
58   writeln("Press a key to stop.");
59   stdin.readln();
60 }
61 
62 /// fast approximate exp function
63 double fastexp(double x) {
64   union U {
65     double d;
66     struct {
67       version(BigEndian) { int32_t i, j; }
68       version(LittleEndian) { int32_t j, i; }
69     };
70   }
71   U u;
72   u.d = 0.0;
73   const double exp_a = 1048756.0 / LN2;
74   const double exp_c = 60801.0;
75   u.i = cast(int32_t)lrint(exp_a * x + (1072693248.0 - exp_c));
76   return u.d;
77 };