commit 1de453e029fad9a1c5a43a15eba306d0cf62bcb4
parent 9ba76539b372bfc6f6215e0a665ebbbd80d598c1
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Thu, 25 Nov 2021 23:01:26 +0000
Consolidate JACK library
Diffstat:
M | .gitignore | | | 3 | ++- |
M | jack-cc-map | | | 0 | |
M | jack-cc-map.cpp | | | 121 | +++++++------------------------------------------------------------------------ |
A | jack-organ.cpp | | | 202 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jack.cpp | | | 114 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | jack.hpp | | | 60 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | main.cpp | | | 329 | ------------------------------------------------------------------------------- |
M | makefile | | | 5 | +++-- |
8 files changed, 391 insertions(+), 443 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -1,2 +1,3 @@
*.[od]
-main
+jack-organ
+jack-cc-map
diff --git a/jack-cc-map b/jack-cc-map
Binary files differ.
diff --git a/jack-cc-map.cpp b/jack-cc-map.cpp
@@ -1,6 +1,6 @@
-#include <jack/jack.h>
#include <jack/midiport.h>
-#include <jack/ringbuffer.h>
+
+#include "jack.hpp"
#include <memory>
#include <cassert>
@@ -27,110 +27,6 @@
using namespace std::literals::chrono_literals;
-namespace jack {
-
-struct client
-{
- client() = default;
- client(char const* name, jack_options_t options)
- : handle(create(name, options), deleter)
- {}
-
- void activate(void)
- {
- assert(not jack_activate(**this));
- }
-
- void set_process_callback(JackProcessCallback cb, void* arg)
- {
- jack_set_process_callback(**this, cb, arg);
- }
- void set_buffer_size_callback(JackBufferSizeCallback cb, void* arg)
- {
- jack_set_buffer_size_callback(**this, cb, arg);
- }
- void set_sample_rate_callback(JackSampleRateCallback cb, void* arg)
- {
- jack_set_sample_rate_callback(**this, cb, arg);
- }
-
-private:
- static jack_client_t* create(char const* name, jack_options_t options)
- {
- jack_status_t status;
- jack_client_t *ptr = jack_client_open(name, options, &status);;
- if (ptr)
- return ptr;
-
- std::exit(1);
- }
-
- static void deleter(jack_client_t* const p)
- {
- assert(not jack_deactivate(p));
- }
-
- std::unique_ptr<jack_client_t, void (*)(jack_client_t*)> handle = {nullptr, deleter};
-
- jack_client_t* operator *(void)
- {
- return handle.get();
- }
-
- friend struct port;
-};
-
-struct port
-{
- port() = default;
-
- port(client& _c, char const* name, char const* type, unsigned long flags, unsigned long buffer_size)
- : c(*_c)
- , p(jack_port_register(c, name, type, flags, buffer_size))
- {
- assert(p);
- }
- ~port()
- {
- if (p)
- jack_port_unregister(c, p);
- }
- port(port const&) = delete;
- port& operator =(port const&) = delete;
-
- port(port&& old)
- : c(old.c)
- , p(old.p)
- {
- old.p = nullptr;
- }
- port& operator =(port&& old)
- {
- if (this != &old)
- {
- if (p)
- jack_port_unregister(c, p);
-
- c = old.c;
- p = old.p;
-
- old.p = nullptr;
- }
- return *this;
- }
-
- jack_port_t* operator *(void)
- {
- return p;
- }
-
-private:
- jack_client_t* c = nullptr;
- jack_port_t* p = nullptr;
-};
-
-}
-
jack::client client("CC-map", JackNoStartServer);
jack::port p1(client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
jack::port p2(client, "out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
@@ -195,7 +91,7 @@ void check_regerror(int err, regex_t* preg)
char buf[256];
regerror(err, preg, buf, sizeof(buf));
- puts(buf);
+ std::cerr << buf << '\n';
exit(1);
}
@@ -211,13 +107,14 @@ int main(int argc, char* argv[])
if (argc < 2)
{
- puts("Usage:\n"
+ std::cerr << "Usage:\n"
" jack-cc-map IN=OUT ...\n\n"
"IN and OUT can either be:\n"
" a number from 0 to 127\n"
" OR\n"
" a symbolic name\n\n"
- "CC messages will be routed from IN to OUT");
+ "CC messages will be routed from IN to OUT\n";
+ return 1;
}
while (*++argv)
@@ -228,11 +125,13 @@ int main(int argc, char* argv[])
int res = regexec(&preg, str, ARRAY_SIZE(pmatch), pmatch, 0);
if (res == REG_NOMATCH)
- continue;
+ {
+ std::cerr << "Bad mapping '" << str << "' is not in format IN=OUT\n";
+ return 1;
+ }
check_regerror(res, &preg);
-
for (unsigned j = 0; j < ARRAY_SIZE(pmatch); ++j)
{
if (pmatch[j].rm_so == -1)
diff --git a/jack-organ.cpp b/jack-organ.cpp
@@ -0,0 +1,202 @@
+#include "jack.hpp"
+
+#include <jack/midiport.h>
+
+#include <cassert>
+
+#include <cmath>
+
+#include <thread>
+#include <chrono>
+#include <iostream>
+#include <iomanip>
+
+using namespace std::literals::chrono_literals;
+
+namespace jack {
+
+}
+
+jack::client client("h-synth", JackNoStartServer);
+jack::port p1(client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+jack::port p2(client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+jack::ringbuffer midi_data(1024);
+
+std::atomic_int midi_events;
+std::atomic<jack_nframes_t> buffer_size, sample_rate;
+
+int cb_buffer_size(jack_nframes_t nframes, void* arg)
+{
+ buffer_size = nframes;
+ return 0;
+}
+int cb_sample_rate(jack_nframes_t nframes, void* arg)
+{
+ sample_rate = nframes;
+ return 0;
+}
+
+static struct voice
+{
+ float envelope, freq, sample;
+ unsigned short active : 1, velocity : 7, state : 3;
+} voices[128];
+
+int cb_process(jack_nframes_t nframes, void* arg)
+{
+ static char sustain;
+ void* buf = jack_port_get_buffer(*p1, nframes);
+
+ jack_midi_event_t event;
+ for (uint32_t i = 0; not jack_midi_event_get(&event, buf, i); ++i)
+ {
+ auto& voice = voices[event.buffer[1]];
+ switch (event.buffer[0] >> 4 & 0x7)
+ {
+ case 0: // Note off
+ voice.active = 0;
+ continue;
+ case 1: // Note on
+ voice.active = 1;
+ voice.state = 4;
+ voice.velocity = event.buffer[2];
+ continue;
+ case 3: // Controller change
+ switch (event.buffer[1])
+ {
+ case 0x40: // Sustain
+ sustain = event.buffer[2] > 63;
+ continue;
+ }
+ /* FALLTHROUGH */
+ default:
+ midi_data.write(event.buffer, event.size);
+ ++midi_events;
+ }
+ }
+
+ auto audio_buf = static_cast<float*>(jack_port_get_buffer(*p2, nframes));
+
+ int srate = sample_rate;
+
+ for (unsigned i = 0; i < buffer_size; ++i)
+ {
+ float accum = 0;
+ for (auto& v : voices)
+ {
+ if (not v.state)
+ continue;
+
+ float pos = v.sample * 6.28318530718f / srate;
+ 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;
+ v.sample += v.freq;
+ if (v.sample >= srate * 6)
+ v.sample -= srate * 6;
+
+ float velocity = v.velocity / 127.f;
+
+ switch (v.state)
+ {
+ case 4:
+ v.envelope += velocity / 1000.f;
+ if (v.envelope >= velocity)
+ {
+ v.envelope = velocity;
+ v.state = 3;
+ }
+ break;
+
+ case 3:
+ v.envelope -= (v.envelope - velocity * 0.2f) * 0.00005f;
+ if (not (sustain || v.active))
+ v.state = 2;
+ break;
+
+ case 2:
+ v.envelope *= 0.9998f;
+ if (v.envelope < 0.01f && sample < 0)
+ v.state = 1;
+ break;
+
+ case 1:
+ v.envelope *= 0.9998f;
+ if (sample >= 0)
+ {
+ v.envelope = 0;
+ v.sample = 0;
+ v.state = 0;
+ }
+ break;
+ }
+ accum += sample * v.envelope * .3;
+ }
+ audio_buf[i] = 2 / (1 + std::exp(accum)) - 1;
+ }
+ return 0;
+}
+
+void print_midi_events(void)
+{
+ static int last = 0;
+ int current = midi_events;
+
+ if (current == last)
+ return;
+
+ last = current;
+ std::cout << "processed " << current << " MIDI events:\n" << std::hex;
+
+ for (unsigned char buf; midi_data.read(&buf, sizeof(buf)); /**/)
+ std::cout << std::setw(3) << +buf;
+
+ std::cout << std::dec << '\n';
+}
+
+void print_buffer_size(void)
+{
+ static int last = 0;
+ int current = buffer_size;
+
+ if (current == last)
+ return;
+
+ last = current;
+ std::cout << "Buffer size changed to: " << current << '\n';
+}
+void print_sample_rate(void)
+{
+ static int last = 0;
+ int current = sample_rate;
+
+ if (current == last)
+ return;
+
+ last = current;
+ std::cout << "Sample rate changed to: " << current << '\n';
+}
+
+int main()
+{
+ int note = 0;
+ for (auto& v : voices)
+ {
+ v.envelope = 0;
+ v.freq = 440 * std::pow(std::pow(2.f, 1.f/12), note++ - 69);
+ }
+
+ client.set_process_callback(cb_process, nullptr);
+ client.set_buffer_size_callback(cb_buffer_size, nullptr);
+ client.set_sample_rate_callback(cb_sample_rate, nullptr);
+ client.activate();
+
+ for (;;)
+ {
+ std::this_thread::sleep_for(10ms);
+ print_buffer_size();
+ print_sample_rate();
+ print_midi_events();
+ }
+
+ return 0;
+}
diff --git a/jack.cpp b/jack.cpp
@@ -0,0 +1,114 @@
+#include "jack.hpp"
+
+#include <cassert>
+
+namespace jack {
+
+// client
+
+client::client(char const* name, jack_options_t options)
+: handle(create(name, options), deleter)
+{}
+
+void client::activate(void)
+{
+ assert(not jack_activate(**this));
+}
+
+void client::set_process_callback(JackProcessCallback cb, void* arg)
+{
+ jack_set_process_callback(**this, cb, arg);
+}
+void client::set_buffer_size_callback(JackBufferSizeCallback cb, void* arg)
+{
+ jack_set_buffer_size_callback(**this, cb, arg);
+}
+void client::set_sample_rate_callback(JackSampleRateCallback cb, void* arg)
+{
+ jack_set_sample_rate_callback(**this, cb, arg);
+}
+
+jack_client_t* client::create(char const* name, jack_options_t options)
+{
+ jack_status_t status;
+ jack_client_t *ptr = jack_client_open(name, options, &status);;
+ if (ptr)
+ return ptr;
+
+ std::exit(1);
+}
+
+void client::deleter(jack_client_t* const p)
+{
+ assert(not jack_deactivate(p));
+}
+
+jack_client_t* client::operator *(void)
+{
+ return handle.get();
+}
+
+// port
+
+port::port(client& _c, char const* name, char const* type, unsigned long flags, unsigned long buffer_size)
+: c(*_c)
+, p(jack_port_register(c, name, type, flags, buffer_size))
+{
+ assert(p);
+}
+port::~port()
+{
+ if (p)
+ jack_port_unregister(c, p);
+}
+
+port::port(port&& old)
+: c(old.c)
+, p(old.p)
+{
+ old.p = nullptr;
+}
+
+port& port::operator =(port&& old)
+{
+ if (this != &old)
+ {
+ if (p)
+ jack_port_unregister(c, p);
+
+ c = old.c;
+ p = old.p;
+
+ old.p = nullptr;
+ }
+ return *this;
+}
+
+jack_port_t* port::operator *(void)
+{
+ return p;
+}
+
+// ringbuffer
+
+ringbuffer::ringbuffer(size_t size)
+: handle(jack_ringbuffer_create(size), jack_ringbuffer_free)
+{}
+
+size_t ringbuffer::write(void const* src, size_t size)
+{
+ return jack_ringbuffer_write(**this, static_cast<char const*>(src), size);
+}
+
+size_t ringbuffer::read(void* dst, size_t size)
+{
+ return jack_ringbuffer_read(**this, static_cast<char*>(dst), size);
+}
+
+jack_ringbuffer_t* ringbuffer::operator *(void)
+{
+ return handle.get();
+}
+
+
+}
diff --git a/jack.hpp b/jack.hpp
@@ -0,0 +1,60 @@
+#pragma once
+#include <memory>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+namespace jack {
+
+struct client
+{
+ client() = default;
+ client(char const* name, jack_options_t options);
+
+ void activate(void);
+ void set_process_callback(JackProcessCallback cb, void* arg);
+ void set_buffer_size_callback(JackBufferSizeCallback cb, void* arg);
+ void set_sample_rate_callback(JackSampleRateCallback cb, void* arg);
+
+private:
+ jack_client_t* operator *(void);
+
+ static jack_client_t* create(char const* name, jack_options_t options);
+ static void deleter(jack_client_t* const p);
+
+ std::unique_ptr<jack_client_t, void (*)(jack_client_t*)> handle = {nullptr, deleter};
+
+ friend struct port;
+};
+
+struct port
+{
+ port() = default;
+ port(client& _c, char const* name, char const* type, unsigned long flags, unsigned long buffer_size);
+ ~port();
+ port(port const&) = delete;
+ port& operator =(port const&) = delete;
+
+ port(port&& old);
+ port& operator =(port&& old);
+
+ jack_port_t* operator *(void);
+
+private:
+ jack_client_t* c = nullptr;
+ jack_port_t* p = nullptr;
+};
+
+struct ringbuffer
+{
+ ringbuffer(size_t size);
+
+ size_t write(void const* src, size_t size);
+ size_t read(void* dst, size_t size);
+
+private:
+ std::unique_ptr<jack_ringbuffer_t, void (*)(jack_ringbuffer_t*)> handle = {nullptr, jack_ringbuffer_free};
+
+ jack_ringbuffer_t* operator *(void);
+};
+
+}
diff --git a/main.cpp b/main.cpp
@@ -1,329 +0,0 @@
-#include <jack/jack.h>
-#include <jack/midiport.h>
-#include <jack/ringbuffer.h>
-
-#include <memory>
-#include <cassert>
-
-#include <cmath>
-
-#include <thread>
-#include <chrono>
-#include <iostream>
-#include <iomanip>
-
-using namespace std::literals::chrono_literals;
-
-namespace jack {
-
-struct client
-{
- client() = default;
- client(char const* name, jack_options_t options)
- : handle(create(name, options), deleter)
- {}
-
- void activate(void)
- {
- assert(not jack_activate(**this));
- }
-
- void set_process_callback(JackProcessCallback cb, void* arg)
- {
- jack_set_process_callback(**this, cb, arg);
- }
- void set_buffer_size_callback(JackBufferSizeCallback cb, void* arg)
- {
- jack_set_buffer_size_callback(**this, cb, arg);
- }
- void set_sample_rate_callback(JackSampleRateCallback cb, void* arg)
- {
- jack_set_sample_rate_callback(**this, cb, arg);
- }
-
-private:
- static jack_client_t* create(char const* name, jack_options_t options)
- {
- jack_status_t status;
- jack_client_t *ptr = jack_client_open(name, options, &status);;
- if (ptr)
- return ptr;
-
- std::exit(1);
- }
-
- static void deleter(jack_client_t* const p)
- {
- assert(not jack_deactivate(p));
- }
-
- std::unique_ptr<jack_client_t, void (*)(jack_client_t*)> handle = {nullptr, deleter};
-
- jack_client_t* operator *(void)
- {
- return handle.get();
- }
-
- friend struct port;
-};
-
-struct port
-{
- port() = default;
-
- port(client& _c, char const* name, char const* type, unsigned long flags, unsigned long buffer_size)
- : c(*_c)
- , p(jack_port_register(c, name, type, flags, buffer_size))
- {
- assert(p);
- }
- ~port()
- {
- if (p)
- jack_port_unregister(c, p);
- }
- port(port const&) = delete;
- port& operator =(port const&) = delete;
-
- port(port&& old)
- : c(old.c)
- , p(old.p)
- {
- old.p = nullptr;
- }
- port& operator =(port&& old)
- {
- if (this != &old)
- {
- if (p)
- jack_port_unregister(c, p);
-
- c = old.c;
- p = old.p;
-
- old.p = nullptr;
- }
- return *this;
- }
-
- jack_port_t* operator *(void)
- {
- return p;
- }
-
-private:
- jack_client_t* c = nullptr;
- jack_port_t* p = nullptr;
-};
-
-struct ringbuffer
-{
- ringbuffer() = default;
- ringbuffer(size_t size)
- : handle(jack_ringbuffer_create(size), jack_ringbuffer_free)
- {}
-
- size_t write(void const* src, size_t size)
- {
- return jack_ringbuffer_write(**this, static_cast<char const*>(src), size);
- }
-
- size_t read(void* dst, size_t size)
- {
- return jack_ringbuffer_read(**this, static_cast<char*>(dst), size);
- }
-
-private:
- std::unique_ptr<jack_ringbuffer_t, void (*)(jack_ringbuffer_t*)> handle = {nullptr, jack_ringbuffer_free};
-
- jack_ringbuffer_t* operator *(void)
- {
- return handle.get();
- }
-};
-
-}
-
-jack::client client("h-synth", JackNoStartServer);
-jack::port p1(client, "in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
-jack::port p2(client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-
-jack::ringbuffer midi_data(1024);
-
-std::atomic_int midi_events;
-std::atomic<jack_nframes_t> buffer_size, sample_rate;
-
-int cb_buffer_size(jack_nframes_t nframes, void* arg)
-{
- buffer_size = nframes;
- return 0;
-}
-int cb_sample_rate(jack_nframes_t nframes, void* arg)
-{
- sample_rate = nframes;
- return 0;
-}
-
-static struct voice
-{
- float envelope, freq, sample;
- unsigned short active : 1, velocity : 7, state : 3;
-} voices[128];
-
-int cb_process(jack_nframes_t nframes, void* arg)
-{
- static char sustain;
- void* buf = jack_port_get_buffer(*p1, nframes);
-
- jack_midi_event_t event;
- for (uint32_t i = 0; not jack_midi_event_get(&event, buf, i); ++i)
- {
- auto& voice = voices[event.buffer[1]];
- switch (event.buffer[0] >> 4 & 0x7)
- {
- case 0: // Note off
- voice.active = 0;
- continue;
- case 1: // Note on
- voice.active = 1;
- voice.state = 4;
- voice.velocity = event.buffer[2];
- continue;
- case 3: // Controller change
- switch (event.buffer[1])
- {
- case 0x40: // Sustain
- sustain = event.buffer[2] > 63;
- continue;
- }
- /* FALLTHROUGH */
- default:
- midi_data.write(event.buffer, event.size);
- ++midi_events;
- }
- }
-
- auto audio_buf = static_cast<float*>(jack_port_get_buffer(*p2, nframes));
-
- int srate = sample_rate;
-
- for (unsigned i = 0; i < buffer_size; ++i)
- {
- float accum = 0;
- for (auto& v : voices)
- {
- if (not v.state)
- continue;
-
- float pos = v.sample * 6.28318530718f / srate;
- 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;
- v.sample += v.freq;
- if (v.sample >= srate * 6)
- v.sample -= srate * 6;
-
- float velocity = v.velocity / 127.f;
-
- switch (v.state)
- {
- case 4:
- v.envelope += velocity / 1000.f;
- if (v.envelope >= velocity)
- {
- v.envelope = velocity;
- v.state = 3;
- }
- break;
-
- case 3:
- v.envelope -= (v.envelope - velocity * 0.2f) * 0.00005f;
- if (not (sustain || v.active))
- v.state = 2;
- break;
-
- case 2:
- v.envelope *= 0.9998f;
- if (v.envelope < 0.01f && sample < 0)
- v.state = 1;
- break;
-
- case 1:
- v.envelope *= 0.9998f;
- if (sample >= 0)
- {
- v.envelope = 0;
- v.sample = 0;
- v.state = 0;
- }
- break;
- }
- accum += sample * v.envelope * .3;
- }
- audio_buf[i] = 2 / (1 + std::exp(accum)) - 1;
- }
- return 0;
-}
-
-void print_midi_events(void)
-{
- static int last = 0;
- int current = midi_events;
-
- if (current == last)
- return;
-
- last = current;
- std::cout << "processed " << current << " MIDI events:\n" << std::hex;
-
- for (unsigned char buf; midi_data.read(&buf, sizeof(buf)); /**/)
- std::cout << std::setw(3) << +buf;
-
- std::cout << std::dec << '\n';
-}
-
-void print_buffer_size(void)
-{
- static int last = 0;
- int current = buffer_size;
-
- if (current == last)
- return;
-
- last = current;
- std::cout << "Buffer size changed to: " << current << '\n';
-}
-void print_sample_rate(void)
-{
- static int last = 0;
- int current = sample_rate;
-
- if (current == last)
- return;
-
- last = current;
- std::cout << "Sample rate changed to: " << current << '\n';
-}
-
-int main()
-{
- int note = 0;
- for (auto& v : voices)
- {
- v.envelope = 0;
- v.freq = 440 * std::pow(std::pow(2.f, 1.f/12), note++ - 69);
- }
-
- client.set_process_callback(cb_process, nullptr);
- client.set_buffer_size_callback(cb_buffer_size, nullptr);
- client.set_sample_rate_callback(cb_sample_rate, nullptr);
- client.activate();
-
- for (;;)
- {
- std::this_thread::sleep_for(10ms);
- print_buffer_size();
- print_sample_rate();
- print_midi_events();
- }
-
- return 0;
-}
diff --git a/makefile b/makefile
@@ -1,11 +1,12 @@
CXXFLAGS := -std=c++20
LDLIBS := -ljack
-targets := main jack-cc-map
+targets := jack-organ jack-cc-map
all: $(targets)
-jack-cc-map: jack-cc-map.cpp names.o
+jack-cc-map: jack-cc-map.cpp names.o jack.cpp
+jack-organ: jack.cpp
clean:
rm $(targets) *.o *.d