From dc05b7c5d1f205d69b35c065e3bd3d7779277943 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sat, 11 Feb 2023 18:59:03 +0100 Subject: [PATCH] Imported existing sources --- .gitmodules | 3 + cxx-ring-buffer | 1 + jackmidiclockinput.pro | 13 ++ main.cpp | 264 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 .gitmodules create mode 160000 cxx-ring-buffer create mode 100644 jackmidiclockinput.pro create mode 100644 main.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..71d5028 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cxx-ring-buffer"] + path = cxx-ring-buffer + url = ../../Ferdi265/cxx-ring-buffer.git diff --git a/cxx-ring-buffer b/cxx-ring-buffer new file mode 160000 index 0000000..d931415 --- /dev/null +++ b/cxx-ring-buffer @@ -0,0 +1 @@ +Subproject commit d9314150c2e1bb508758f75cabab043238bae07b diff --git a/jackmidiclockinput.pro b/jackmidiclockinput.pro new file mode 100644 index 0000000..8a2ed82 --- /dev/null +++ b/jackmidiclockinput.pro @@ -0,0 +1,13 @@ +QT = +CONFIG += c++latest +LIBS += -ljack -lfmt +DEFINES += \ + RING_BUFFER_CONSTEXPR \ + RING_BUFFER_NOEXCEPT \ + RING_BUFFER_CONSTEXPR_DESTRUCTORS +INCLUDEPATH += cxx-ring-buffer/include +HEADERS += \ + cxx-ring-buffer/include/ring-buffer-config.h \ + cxx-ring-buffer/include/ring-buffer-iterator.h \ + cxx-ring-buffer/include/ring-buffer.h +SOURCES += main.cpp diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..1890710 --- /dev/null +++ b/main.cpp @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +using namespace std::chrono_literals; + +namespace { +struct captures_t { + jack_client_t *client; + jack_port_t *midi_in_port; + jack_port_t *audio_out_port; +}; + +const char *toString(jack_transport_state_t val) +{ + switch (val) + { + case JackTransportStopped: return "Stopped"; + case JackTransportRolling: return "Rolling"; + case JackTransportLooping: return "Looping"; + case JackTransportStarting: return "Starting"; + case JackTransportNetStarting: return "NetStarting"; + } + + __builtin_unreachable(); +} + +std::atomic bar; /**< current bar */ +std::atomic beat; /**< current beat-within-bar */ +std::atomic tick; /**< current tick-within-beat */ +std::atomic beats_per_minute; + +std::chrono::steady_clock::time_point lastTimebaseCallback; +} // namespace + +int main(int argc, char *argv[]) +{ + std::printf("Start\n"); + + std::unique_ptr client {{}, &jack_client_close}; + + { + const char *client_name = "biepometer"; + jack_options_t options = JackNoStartServer; + jack_status_t status; + const char *serverName = nullptr; + + client = std::unique_ptr{ + jack_client_open(client_name, options, &status, serverName), + &jack_client_close + }; + if (!client) + throw std::runtime_error{"no jack client"}; + if (status & JackNameNotUnique) + std::printf("du depp du ned unique: %s\n", jack_get_client_name(client.get())); + } + + std::mutex mutex; + mutex.lock(); + + jack_nframes_t sampleRate = jack_get_sample_rate(client.get()); + std::printf("sampleRate=%u\n", sampleRate); + + jack_nframes_t bufferSize = jack_get_buffer_size(client.get()); + std::printf("bufferSize=%u\n", bufferSize); + + auto unregisterCb = [&client](jack_port_t *arg){ jack_port_unregister(client.get(), arg); }; + + auto midi_in_port = std::unique_ptr { + jack_port_register(client.get(), "midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0), + unregisterCb + }; + if (!midi_in_port) + throw std::runtime_error{"could not register midi in port"}; + std::printf("midi_in_port = %p\n", midi_in_port.get()); + + auto audio_out_port = std::unique_ptr { + jack_port_register(client.get(), "audio_out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0), + std::move(unregisterCb) + }; + if (!audio_out_port) + throw std::runtime_error{"could not register audio out port"}; + std::printf("audio_out_port = %p\n", audio_out_port.get()); + + captures_t captures { client.get(), midi_in_port.get(), audio_out_port.get() }; + + if (const auto result = jack_set_process_callback(client.get(), + [](jack_nframes_t nframes, void *arg)->int{ + const captures_t *captures = (const captures_t *)arg; +// std::printf("%s callback %u\n", "", nframes); + + jack_position_t position; + jack_transport_state_t transport = jack_transport_query(captures->client, &position); +// std::printf("transport=%s bar=%i beat=%i tick=%i\n", toString(transport), position.bar, position.beat, position.tick); + + jack_nframes_t sample_rate = jack_get_sample_rate(captures->client); + + auto sample_interval = (1000000000ULL + (sample_rate/2)) / sample_rate; + + { + void *inputPortBuf = jack_port_get_buffer(captures->midi_in_port, nframes); + + jack_nframes_t event_count = jack_midi_get_event_count(inputPortBuf); + + for (jack_nframes_t i = 0; i < event_count; i++) + { + jack_midi_event_t in_event; + if (const auto result = jack_midi_event_get(&in_event, inputPortBuf, i); result != 0) + { + std::printf("jack_midi_event_get() failed with %i\n", result); + continue; + } + + static uint64_t lastClock{}; + + switch (in_event.size) + { + case 0: + std::printf("midi event %u\n", in_event.time); + break; + case 1: + // std::printf("midi event %u %hhu\n", in_event.time, in_event.buffer[0]); + switch (in_event.buffer[0]) + { + case 248: + { + const uint64_t absolute_timestamp = position.usecs + (sample_interval * in_event.time / 1000); + const double bpm = int(60000000./(absolute_timestamp - lastClock) / 24 * 10) / 10.; + static ring_buffer bpmBuffer; + bpmBuffer.push_back(bpm); + beats_per_minute = + std::accumulate(std::cbegin(bpmBuffer), std::cend(bpmBuffer), 0.) / + std::size(bpmBuffer); + // std::printf("clock usecs=%lu delta=%lu bpm=%.1f sample_rate=%u sample_interval=%llu\n", absolute_timestamp, absolute_timestamp - lastClock, bpm, sample_rate, sample_interval); + if (false && std::chrono::steady_clock::now() - lastTimebaseCallback > 1s) + { + std::printf("Registering timebase callback...\n"); + jack_transport_start(captures->client); + lastTimebaseCallback = std::chrono::steady_clock::now(); + if (const auto result = jack_set_timebase_callback(captures->client, 0, [](jack_transport_state_t state, + jack_nframes_t nframes, + jack_position_t *pos, + int new_pos, + void *arg){ + lastTimebaseCallback = std::chrono::steady_clock::now(); + pos->valid = JackPositionBBT; + + pos->bar = bar; + pos->beat = beat; + pos->tick = tick; + + pos->bar_start_tick = 0; + + pos->beats_per_bar = 4; + pos->beat_type = 0; + pos->ticks_per_beat = 24; + pos->beats_per_minute = beats_per_minute; + + std::printf("timebase_callback(): state=%s nframes=%u bar=%i beat=%i tick=%i bpm=%f\n", toString(state), nframes, pos->bar, pos->beat, pos->tick, pos->beats_per_minute); + }, nullptr); result != 0) + throw std::runtime_error{fmt::format("jack_set_timebase_callback() failed with {}", result)}; + } + lastClock = absolute_timestamp; + tick++; + if (tick >= 24) + { + tick = 0; + beat++; + if (beat >= 4) + { + beat = 0; + bar++; + } + } + break; + } + case 250: // midi start + std::printf("midi start received\n"); + bar = 0; + beat = 0; + tick = 0; + break; + case 252: // midi stop + std::printf("midi stop received\n"); + break; + default: + std::printf("unknown 1-byte message %hhu\n", in_event.buffer[0]); + } + break; + case 2: + std::printf("midi event %hhu %hhu\n", in_event.buffer[0], in_event.buffer[1]); + break; + case 3: + std::printf("midi event %hhu %hhu %hhu\n", in_event.buffer[0], in_event.buffer[1], in_event.buffer[2]); + break; + case 4: + std::printf("midi event %hhu %hhu %hhu %hhu\n", in_event.buffer[0], in_event.buffer[1], in_event.buffer[2], in_event.buffer[3]); + break; + case 5: + std::printf("midi event %hhu %hhu %hhu %hhu %hhu\n", in_event.buffer[0], in_event.buffer[1], in_event.buffer[2], in_event.buffer[3], in_event.buffer[4]); + break; + } + } + } + + { + jack_default_audio_sample_t *outputPortBuf = (jack_default_audio_sample_t*)jack_port_get_buffer(captures->audio_out_port, nframes); + for (jack_nframes_t i = 0; i < nframes; i++) + { + static jack_nframes_t j{}; + switch (tick) + { + case 0: +// case 1: + case 12: +// case 13: + outputPortBuf[i] = (float(j++ % 512) / 512.f) - 1.f; + if (j >= 512) + j = 0; + break; + default: + outputPortBuf[i] = 0.f; + break; + } + + } + } + + return {}; + }, &captures); result != 0) + throw std::runtime_error{fmt::format("jack_set_process_callback() failed with {}", result)}; + + if (const auto result = jack_activate(client.get()); result != 0) + throw std::runtime_error{fmt::format("jack_activate() failed with {}", result)}; + auto unactivateCb = [&client](void *){ + std::printf("deactivate...\n"); + if (const auto result = jack_deactivate(client.get()); result != 0) + std::printf("jack_deactivate() failed with %i\n", result); + }; + auto activateGuard = std::unique_ptr{(void*)1, unactivateCb}; + + jack_on_shutdown(client.get(), [](void *arg){ + std::printf("shutdown received\n"); + std::mutex *mutex = (std::mutex *)arg; + mutex->unlock(); + }, &mutex); + + std::printf("application working...\n"); + mutex.lock(); + + std::printf("main thread ending\n"); + mutex.unlock(); +}