From c31e2f195e2a4bdd93a402e78a0f6919c2606972 Mon Sep 17 00:00:00 2001 From: brummer10 Date: Wed, 16 Oct 2024 08:39:56 +0200 Subject: [PATCH] Some code cleanup --- src/AnimatedKeyBoard.cpp | 71 +++++++ src/AnimatedKeyBoard.h | 56 ++++++ src/CustomDrawings.h | 9 + src/Makefile | 3 +- src/MidiKeyBoard.cpp | 391 ++++--------------------------------- src/MidiKeyBoard.h | 103 ++-------- src/PosixSignalHandler.cpp | 103 ++++++++++ src/PosixSignalHandler.h | 59 ++++++ src/main.cpp | 166 ++++++++++++++++ 9 files changed, 518 insertions(+), 443 deletions(-) create mode 100644 src/AnimatedKeyBoard.cpp create mode 100644 src/AnimatedKeyBoard.h create mode 100644 src/PosixSignalHandler.cpp create mode 100644 src/PosixSignalHandler.h create mode 100644 src/main.cpp diff --git a/src/AnimatedKeyBoard.cpp b/src/AnimatedKeyBoard.cpp new file mode 100644 index 0000000..40d9045 --- /dev/null +++ b/src/AnimatedKeyBoard.cpp @@ -0,0 +1,71 @@ +/* + * 0BSD + * + * BSD Zero Clause License + * + * Copyright (c) 2020 Hermann Meyer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + */ + + +#include "AnimatedKeyBoard.h" + + +/**************************************************************** + ** class AnimatedKeyBoard + ** + ** animate midi input from jack on the keyboard in a extra thread + ** + */ + +namespace animatedkeyboard { + +AnimatedKeyBoard::AnimatedKeyBoard() + :_execute(false) { +} + +AnimatedKeyBoard::~AnimatedKeyBoard() { + if( _execute.load(std::memory_order_acquire) ) { + stop(); + }; +} + +void AnimatedKeyBoard::stop() { + _execute.store(false, std::memory_order_release); + if (_thd.joinable()) { + _thd.join(); + } +} + +void AnimatedKeyBoard::start(int interval, std::function func) { + if( _execute.load(std::memory_order_acquire) ) { + stop(); + }; + _execute.store(true, std::memory_order_release); + _thd = std::thread([this, interval, func]() { + while (_execute.load(std::memory_order_acquire)) { + func(); + std::this_thread::sleep_for( + std::chrono::milliseconds(interval)); + } + }); +} + +bool AnimatedKeyBoard::is_running() const noexcept { + return ( _execute.load(std::memory_order_acquire) && + _thd.joinable() ); +} + +} // namespace animatedkeyboard + diff --git a/src/AnimatedKeyBoard.h b/src/AnimatedKeyBoard.h new file mode 100644 index 0000000..e3c3400 --- /dev/null +++ b/src/AnimatedKeyBoard.h @@ -0,0 +1,56 @@ +/* + * 0BSD + * + * BSD Zero Clause License + * + * Copyright (c) 2020 Hermann Meyer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + */ + + +#include +#include +#include +#include + +#pragma once + +#ifndef ANIMATEDKEYBOARD_H +#define ANIMATEDKEYBOARD_H + +/**************************************************************** + ** class AnimatedKeyBoard + ** + ** animate midi input from jack on the keyboard in a extra thread + ** + */ + +namespace animatedkeyboard { + +class AnimatedKeyBoard { +private: + std::atomic _execute; + std::thread _thd; + +public: + AnimatedKeyBoard(); + ~AnimatedKeyBoard(); + void stop(); + void start(int interval, std::function func); + bool is_running() const noexcept; +}; + +} // namespace animatedkeyboard + +#endif diff --git a/src/CustomDrawings.h b/src/CustomDrawings.h index 6522b6f..2618664 100644 --- a/src/CustomDrawings.h +++ b/src/CustomDrawings.h @@ -25,7 +25,16 @@ #ifndef CUSTOMDRAWINGS_H #define CUSTOMDRAWINGS_H + +/**************************************************************** + ** class CustomDrawings + ** + ** drawing routines from Mamba - Virtual Midi Keyboard + ** + */ + namespace midikeyboard { + class CustomDrawings { public: diff --git a/src/Makefile b/src/Makefile index 9fef016..0f8d373 100644 --- a/src/Makefile +++ b/src/Makefile @@ -80,7 +80,8 @@ endif `pkg-config --cflags jack cairo x11 sigc++-2.0 liblo smf fluidsynth`\ -DVERSION=\"$(VER)\" # invoke build files - OBJECTS = $(OLDNAME).cpp $(NAME).cpp XAlsa.cpp XJack.cpp NsmHandler.cpp XSynth.cpp MidiMapper.cpp + OBJECTS = $(NAME).cpp XAlsa.cpp XJack.cpp NsmHandler.cpp XSynth.cpp MidiMapper.cpp main.cpp \ + PosixSignalHandler.cpp AnimatedKeyBoard.cpp $(OLDNAME).cpp SOBJECTS = $(LIBSCALA_DIR)scala_kbm.cpp $(LIBSCALA_DIR)scala_scl.cpp COBJECTS = xmkeyboard.c xcustommap.c OBJ_FILES = $(addprefix ./$(BUILD_DIR)/,$(notdir $(OBJECTS:.cpp=.o))) diff --git a/src/MidiKeyBoard.cpp b/src/MidiKeyBoard.cpp index 8bff893..5456924 100644 --- a/src/MidiKeyBoard.cpp +++ b/src/MidiKeyBoard.cpp @@ -24,53 +24,6 @@ #include "xcustommap.h" -namespace midikeyboard { - - -/**************************************************************** - ** class AnimatedKeyBoard - ** - ** animate midi input from jack on the keyboard in a extra thread - ** - */ - -AnimatedKeyBoard::AnimatedKeyBoard() - :_execute(false) { -} - -AnimatedKeyBoard::~AnimatedKeyBoard() { - if( _execute.load(std::memory_order_acquire) ) { - stop(); - }; -} - -void AnimatedKeyBoard::stop() { - _execute.store(false, std::memory_order_release); - if (_thd.joinable()) { - _thd.join(); - } -} - -void AnimatedKeyBoard::start(int interval, std::function func) { - if( _execute.load(std::memory_order_acquire) ) { - stop(); - }; - _execute.store(true, std::memory_order_release); - _thd = std::thread([this, interval, func]() { - while (_execute.load(std::memory_order_acquire)) { - func(); - std::this_thread::sleep_for( - std::chrono::milliseconds(interval)); - } - }); -} - -bool AnimatedKeyBoard::is_running() const noexcept { - return ( _execute.load(std::memory_order_acquire) && - _thd.joinable() ); -} - - /**************************************************************** ** class XKeyBoard ** @@ -78,10 +31,12 @@ bool AnimatedKeyBoard::is_running() const noexcept { ** */ +namespace midikeyboard { + XKeyBoard::XKeyBoard(xjack::XJack *xjack_, xalsa::XAlsa *xalsa_, xsynth::XSynth *xsynth_, midimapper::MidiMapper *midimap, mamba::MidiMessenger *mmessage_, nsmhandler::NsmSignalHandler& nsmsig_, - PosixSignalHandler& xsig_, AnimatedKeyBoard * animidi_) + signalhandler::PosixSignalHandler& xsig_, animatedkeyboard::AnimatedKeyBoard * animidi_) : xalsa(xalsa_), xsynth(xsynth_), mmapper(midimap), @@ -1005,64 +960,66 @@ void XKeyBoard::init_ui(Xputty *app) { knob_box->func.key_release_callback = key_release; w[0] = mamba_add_keyboard_knob(knob_box, _("PitchBend"), 3, 0, 60, 75); - w[0]->data = PITCHBEND; w[0]->func.value_changed_callback = pitchwheel_callback; w[0]->func.button_release_callback = pitchwheel_release_callback; w[0]->func.button_press_callback = pitchwheel_press_callback; w[9] = mamba_add_keyboard_knob(knob_box, _("Balance"), 63, 0, 60, 75); - w[9]->data = BALANCE; - w[9]->func.value_changed_callback = balance_callback; + w[9]->data = 8; + w[9]->func.value_changed_callback = midi_cc_callback; w[1] = mamba_add_keyboard_knob(knob_box, _("ModWheel"), 123, 0, 60, 75); - w[1]->data = MODULATION; + w[1]->data = 1; set_adjustment(w[1]->adj, 0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[1]->func.value_changed_callback = modwheel_callback; + w[1]->func.value_changed_callback = midi_cc_callback; w[2] = mamba_add_keyboard_knob(knob_box, _("Detune"), 183, 0, 60, 75); - w[2]->data = CELESTE; - w[2]->func.value_changed_callback = detune_callback; + w[2]->data = 94; + w[2]->func.value_changed_callback = midi_cc_callback; w[10] = mamba_add_keyboard_knob(knob_box, _("Expression"), 243, 0, 60, 75); - w[10]->data = EXPRESSION; + w[10]->data = 11; + w[10]->private_struct = (void*)expresion; set_adjustment(w[10]->adj, 127.0, 127.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[10]->func.value_changed_callback = expression_callback; + w[10]->func.value_changed_callback = midi_cc_channel_callback; w[3] = mamba_add_keyboard_knob(knob_box, _("Attack"), 303, 0, 60, 75); - w[3]->data = ATTACK_TIME; + w[3]->data = 73; + w[3]->private_struct = (void*)attack; set_adjustment(w[3]->adj,0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[3]->func.value_changed_callback = attack_callback; + w[3]->func.value_changed_callback = midi_cc_channel_callback; w[4] = mamba_add_keyboard_knob(knob_box, _("Release"), 363, 0, 60, 75); - w[4]->data = RELEASE_TIME; + w[4]->data = 72; + w[4]->private_struct = (void*)release; set_adjustment(w[4]->adj,0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[4]->func.value_changed_callback = release_callback; + w[4]->func.value_changed_callback = midi_cc_channel_callback; w[8] = mamba_add_keyboard_knob(knob_box, _("Cutoff"), 423, 0, 60, 75); + w[8]->data = 74; + w[8]->private_struct = (void*)cutoff; set_adjustment(w[8]->adj,0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[8]->func.value_changed_callback = cutoff_callback; + w[8]->func.value_changed_callback = midi_cc_channel_callback; w[11] = mamba_add_keyboard_knob(knob_box, _("Resonance"), 483, 0, 60, 75); + w[11]->data = 71; + w[11]->private_struct = (void*)resonance; set_adjustment(w[11]->adj,0.0, 0.0, 0.0, 127.0, 1.0, CL_CONTINUOS); - w[11]->func.value_changed_callback = resonance_callback; + w[11]->func.value_changed_callback = midi_cc_channel_callback; w[5] = mamba_add_keyboard_knob(knob_box, _("Volume"), 543, 0, 60, 75); - w[5]->data = VOLUME; - w[5]->func.value_changed_callback = volume_callback; + w[5]->data = 7; + w[5]->private_struct = (void*)volume; + w[5]->func.value_changed_callback = midi_cc_channel_callback; w[6] = mamba_add_keyboard_knob(knob_box, _("Velocity"), 603, 0, 60, 75); - w[6]->data = VELOCITY; set_adjustment(w[6]->adj, 127.0, 127.0, 0.0, 127.0, 1.0, CL_CONTINUOS); w[6]->func.value_changed_callback = velocity_callback; w[7] = mamba_add_keyboard_switch(knob_box, _("Sustain"), 663, 9, 34, 70); - w[7]->data = SUSTAIN; + w[7]->data = 64; w[7]->func.value_changed_callback = sustain_callback; -/* - w[8] = mamba_add_keyboard_button(knob_box, _("Sostenuto"), 550, 45, 75, 30); - w[8]->data = SOSTENUTO; - w[8]->func.value_changed_callback = sostenuto_callback; -*/ + // open a widget for the keyboard layout int wpos = view_controls ? 147 : 70; if (!view_program) wpos -= 45; @@ -2216,65 +2173,19 @@ void XKeyBoard::bpm_callback(void *w_, void* user_data) noexcept{ } // static -void XKeyBoard::modwheel_callback(void *w_, void* user_data) noexcept{ +void XKeyBoard::midi_cc_callback(void *w_, void* user_data) noexcept{ Widget_t *w = (Widget_t*)w_; int value = (int)adj_get_value(w->adj); - XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xB0, 1, value, 3, false); -} - -// static -void XKeyBoard::detune_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - int value = (int)adj_get_value(w->adj); - XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xB0, 94, value, 3, false); -} - -// static -void XKeyBoard::attack_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->attack[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, 73, xjmkb->attack[xjmkb->mchannel], 3, true); + XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xB0, w->data, value, 3, false); } // static -void XKeyBoard::expression_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->expresion[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0| xjmkb->mchannel , 11, xjmkb->expresion[xjmkb->mchannel], 3, true); -} - -// static -void XKeyBoard::release_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->release[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0| xjmkb->mchannel, 72, xjmkb->release[xjmkb->mchannel], 3, true); -} - -// static -void XKeyBoard::cutoff_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->cutoff[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, 74, xjmkb->cutoff[xjmkb->mchannel], 3, true); -} - -// static -void XKeyBoard::resonance_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->resonance[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, 71, xjmkb->resonance[xjmkb->mchannel], 3, true); -} - -// static -void XKeyBoard::volume_callback(void *w_, void* user_data) noexcept{ +void XKeyBoard::midi_cc_channel_callback(void *w_, void* user_data) noexcept{ Widget_t *w = (Widget_t*)w_; XKeyBoard *xjmkb = XKeyBoard::get_instance(w); - xjmkb->volume[xjmkb->mchannel] = (int)adj_get_value(w->adj); - xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, 7, xjmkb->volume[xjmkb->mchannel], 3, true); + int *value = (int*)w->private_struct; + value[xjmkb->mchannel] = (int)adj_get_value(w->adj); + xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, w->data, value[xjmkb->mchannel], 3, true); } // static @@ -2329,13 +2240,6 @@ void XKeyBoard::pitchwheel_release_callback(void *w_, void* button, void* user_d XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xE0, low, high, 3, false); } -// static -void XKeyBoard::balance_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - int value = (int)adj_get_value(w->adj); - XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xB0, 8, value, 3, false); -} - // static void XKeyBoard::sustain_callback(void *w_, void* user_data) noexcept{ Widget_t *w = (Widget_t*)w_; @@ -2344,13 +2248,6 @@ void XKeyBoard::sustain_callback(void *w_, void* user_data) noexcept{ xjmkb->mmessage->send_midi_cc(0xB0 | xjmkb->mchannel, 64, xjmkb->sustain[xjmkb->mchannel]*127, 3, true); } -// static -void XKeyBoard::sostenuto_callback(void *w_, void* user_data) noexcept{ - Widget_t *w = (Widget_t*)w_; - int value = (int)adj_get_value(w->adj); - XKeyBoard::get_instance(w)->mmessage->send_midi_cc(0xB0, 66, value*127, 3, false); -} - void XKeyBoard::find_next_beat_time(double *absoluteTime) { const double beat = 60.0/(double)mbpm; int beats = std::round(((*absoluteTime)/beat)); @@ -3517,223 +3414,5 @@ void XKeyBoard::exit_handle (int sig) { exit (0); } -/**************************************************************** - ** class PosixSignalHandler - ** - ** Watch for incomming system signals in a extra thread - ** - */ - -PosixSignalHandler::PosixSignalHandler() - : sigc::trackable(), - waitset(), - thread(nullptr), - exit(false) { - sigemptyset(&waitset); - - sigaddset(&waitset, SIGINT); - sigaddset(&waitset, SIGQUIT); - sigaddset(&waitset, SIGTERM); - sigaddset(&waitset, SIGHUP); - sigaddset(&waitset, SIGKILL); - - sigprocmask(SIG_BLOCK, &waitset, NULL); - create_thread(); -} - -PosixSignalHandler::~PosixSignalHandler() { - if (thread) { - exit = true; - pthread_kill(thread->native_handle(), SIGINT); - thread->join(); - delete thread; - } - sigprocmask(SIG_UNBLOCK, &waitset, NULL); -} - -void PosixSignalHandler::create_thread() { - try { - thread = new std::thread( - sigc::mem_fun(*this, &PosixSignalHandler::signal_helper_thread)); - } catch (std::system_error& e) { - fprintf(stderr,"Thread create failed (signal): %s", e.what()); - } -} - -void PosixSignalHandler::signal_helper_thread() { - - pthread_sigmask(SIG_BLOCK, &waitset, NULL); - while (true) { - int sig; - int ret = sigwait(&waitset, &sig); - if (exit) { - break; - } - if (ret != 0) { - assert(errno == EINTR); - continue; - } - switch (sig) { - case SIGINT: - case SIGTERM: - case SIGQUIT: - trigger_quit_by_posix(sig); - break; - case SIGHUP: - case SIGKILL: - trigger_kill_by_posix(sig); - break; - default: - break; - } - } -} - } // namespace midikeyboard - -/**************************************************************** - ** main - ** - ** init the classes, create the UI, open jackd-client and start application - ** - */ - -int main (int argc, char *argv[]) { - auto t1 = std::chrono::high_resolution_clock::now(); - -#ifdef ENABLE_NLS - // set Message type to locale to fetch localisation support - std::setlocale (LC_MESSAGES, ""); - // set Ctype to C to avoid symbol clashes from different locales - std::setlocale (LC_CTYPE, "C"); - bindtextdomain(GETTEXT_PACKAGE, LOCAL_DIR); - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); - textdomain(GETTEXT_PACKAGE); -#endif - - if(0 == XInitThreads()) - fprintf(stderr, "Warning: XInitThreads() failed\n"); - - midikeyboard::PosixSignalHandler xsig; - - Xputty app; - - mamba::MidiMessenger mmessage; - nsmhandler::NsmSignalHandler nsmsig; - midikeyboard::AnimatedKeyBoard animidi; - - midimapper::MidiMapper midimap([&mmessage] - (int _cc, int _pg, int _bgn, int _num, bool have_channel) noexcept - {mmessage.send_midi_cc( _cc, _pg, _bgn, _num, have_channel);}); - - xalsa::XAlsa xalsa([&mmessage] - (int _cc, int _pg, int _bgn, int _num, bool have_channel) noexcept - {mmessage.send_midi_cc( _cc, _pg, _bgn, _num, have_channel);}, - [&midimap] (const uint8_t* m ,uint8_t n ) noexcept {midimap.mmapper_input_notify(m,n);}); - - xjack::XJack xjack(&mmessage, - [&xalsa] (const uint8_t* m ,uint8_t n ) noexcept {xalsa.xalsa_output_notify(m,n);}, - [&xalsa] (int p ) {xalsa.xalsa_set_priority(p);}, - [&midimap] (const uint8_t* m ,uint8_t n ) noexcept {midimap.mmapper_input_notify(m,n);}, - [&midimap] (int p ) {midimap.mmapper_set_priority(p);}); - - xsynth::XSynth xsynth; - midikeyboard::XKeyBoard xjmkb(&xjack, &xalsa, &xsynth, &midimap, &mmessage, nsmsig, xsig, &animidi); - nsmhandler::NsmHandler nsmh(&nsmsig); - - nsmsig.nsm_session_control = nsmh.check_nsm(xjmkb.client_name.c_str(), argv); - - main_init(&app); - - if (xjack.init_jack()) { - - if (!nsmsig.nsm_session_control) - xjmkb.set_config_file(); - - xjmkb.read_config(); - xjmkb.init_ui(&app); - MambaKeyboard *keys = (MambaKeyboard*)xjmkb.wid->parent_struct; - midimap.mmapper_start([keys] (int channel, int key, bool set) - {mamba_set_key_in_matrix(keys->in_key_matrix[channel], key, set);}); - if (xalsa.xalsa_init(xjack.client_name.c_str(), "input", "output") >= 0) { - xalsa.xalsa_start([keys] (int channel, int key, bool set) - {mamba_set_key_in_matrix(keys->in_key_matrix[channel], key, set);}); - } else { - fprintf(stderr, _("Couldn't open a alsa port, is the alsa sequencer running?\n")); - } - - if (!xjmkb.soundfont.empty()) { - std::string synth_instance = xjack.client_name; - std::transform(synth_instance.begin(), synth_instance.end(), synth_instance.begin(), ::tolower); - xsynth.setup(xjack.SampleRate, synth_instance.c_str()); - xsynth.init_synth(); - xsynth.load_soundfont(xjmkb.soundfont.c_str()); - const char **port_list = NULL; - port_list = jack_get_ports(xjack.client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput); - if (port_list) { - for (int i = 0; port_list[i] != NULL; i++) { - if (strstr(port_list[i], synth_instance.c_str())) { - const char *my_port = jack_port_name(xjack.out_port); - jack_connect(xjack.client, my_port,port_list[i]); - break; - } - } - jack_free(port_list); - port_list = NULL; - } - for (int i = 0; i<16;i++) - mmessage.send_midi_cc(0xB0 | i, 7, xjmkb.volume[i], 3, true); - xjmkb.fs[0]->state = 0; - xjmkb.fs[1]->state = 0; - xjmkb.fs[2]->state = 0; - xjmkb.fs[3]->state = 0; - xjmkb.rebuild_instrument_list(); - xjmkb.rebuild_soundfont_list(); - xjmkb.init_modulators(&xjmkb); - } - - if (argc > 1) { - -#ifdef __XDG_MIME_H__ - if(strstr(xdg_mime_get_mime_type_from_file_name(argv[1]), "midi")) { -#else - if( access(argv[1], F_OK ) != -1 ) { -#endif - xjmkb.dialog_load_response(xjmkb.win, (void*) &argv[1]); - } - } - - if (xsynth.synth_is_active()) { - for(std::vector::iterator i = xjack.rec.play[0].begin(); i != xjack.rec.play[0].end(); ++i) { - if (((*i).buffer[0] & 0xf0) == 0xB0 && ((*i).buffer[1]== 32 || (*i).buffer[1]== 0)) { - mmessage.send_midi_cc((*i).buffer[0], (*i).buffer[1], (*i).buffer[2], 3, true); - } else if (((*i).buffer[0] & 0xf0) == 0xC0 ) { - mmessage.send_midi_cc((*i).buffer[0], (*i).buffer[1], 0, 2, true); - } - } - } - xjmkb.client_name = xjack.client_name; - std::string tittle = xjmkb.client_name + _(" - Virtual Midi Keyboard"); - widget_set_title(xjmkb.win, tittle.c_str()); - xjmkb.show_ui(xjmkb.visible); - if (xsynth.synth_is_active()) xjmkb.rebuild_instrument_list(); - auto t2 = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast( t2 - t1 ).count(); - debug_print("%f sec\n",duration/1e+6); - - main_run(&app); - - animidi.stop(); - if (xjack.client) jack_client_close (xjack.client); - xsynth.unload_synth(); - if(!nsmsig.nsm_session_control) xjmkb.save_config(); - } - main_quit(&app); - - exit (0); - -} - - - diff --git a/src/MidiKeyBoard.h b/src/MidiKeyBoard.h index d0f7bb4..489bbbd 100644 --- a/src/MidiKeyBoard.h +++ b/src/MidiKeyBoard.h @@ -18,6 +18,7 @@ * */ +#include "AnimatedKeyBoard.h" #include #include @@ -48,6 +49,7 @@ #endif #include "NsmHandler.h" +#include "PosixSignalHandler.h" #include "Mamba.h" #include "XJack.h" #include "XAlsa.h" @@ -69,71 +71,6 @@ namespace midikeyboard { #define CPORTS 12 -typedef enum { - PITCHBEND, - MODULATION, - CELESTE, - ATTACK_TIME, - RELEASE_TIME, - VOLUME, - VELOCITY, - SUSTAIN, - SOSTENUTO, - BALANCE, - EXPRESSION, - KEYMAP, - LAYOUT, - -}ControlPorts; - - -/**************************************************************** - ** class PosixSignalHandler - ** - ** Watch for incomming system signals in a extra thread - ** - */ - -class PosixSignalHandler : public sigc::trackable { -private: - sigset_t waitset; - std::thread *thread; - volatile bool exit; - void signal_helper_thread(); - void create_thread(); - -public: - PosixSignalHandler(); - ~PosixSignalHandler(); - - sigc::signal trigger_quit_by_posix; - sigc::signal& signal_trigger_quit_by_posix() { return trigger_quit_by_posix; } - - sigc::signal trigger_kill_by_posix; - sigc::signal& signal_trigger_kill_by_posix() { return trigger_kill_by_posix; } -}; - -/**************************************************************** - ** class AnimatedKeyBoard - ** - ** animate midi input from jack on the keyboard in a extra thread - ** - */ - -class AnimatedKeyBoard { -private: - std::atomic _execute; - std::thread _thd; - -public: - AnimatedKeyBoard(); - ~AnimatedKeyBoard(); - void stop(); - void start(int interval, std::function func); - bool is_running() const noexcept; -}; - - /**************************************************************** ** class XKeyBoard ** @@ -149,9 +86,9 @@ class XKeyBoard { mamba::MidiSave save; mamba::MidiLoad load; mamba::MidiMessenger *mmessage; - AnimatedKeyBoard * animidi; + animatedkeyboard::AnimatedKeyBoard * animidi; nsmhandler::NsmSignalHandler& nsmsig; - PosixSignalHandler& xsig; + signalhandler::PosixSignalHandler& xsig; CustomDrawings draw; Widget_t *w[CPORTS]; @@ -244,6 +181,18 @@ class XKeyBoard { static void get_note(Widget_t *w, const int *key, const bool on_off) noexcept; static void get_all_notes_off(Widget_t *w, const int *value) noexcept; + // midi cc controller callbacks + static void midi_cc_callback(void *w_, void* user_data) noexcept; + static void midi_cc_channel_callback(void *w_, void* user_data) noexcept; + static void bank_callback(void *w_, void* user_data) noexcept; + static void program_callback(void *w_, void* user_data); + static void bpm_callback(void *w_, void* user_data) noexcept; + static void velocity_callback(void *w_, void* user_data) noexcept; + static void pitchwheel_callback(void *w_, void* user_data) noexcept; + static void pitchwheel_release_callback(void *w_, void* button, void* user_data) noexcept; + static void pitchwheel_press_callback(void *w_, void* button, void* user_data) noexcept; + static void sustain_callback(void *w_, void* user_data) noexcept; + static void set_std_value(void *w_, void* button, void* user_data) noexcept; static void info_callback(void *w_, void* user_data); static void load_scala_callback(void *w_, void* user_data); @@ -257,9 +206,6 @@ class XKeyBoard { static void file_remove_callback(void *w_, void* user_data); static void rebuild_remove_menu(void *w_, void* button, void* user_data); static void channel_callback(void *w_, void* user_data) noexcept; - static void bank_callback(void *w_, void* user_data) noexcept; - static void program_callback(void *w_, void* user_data); - static void bpm_callback(void *w_, void* user_data) noexcept; static void layout_callback(void *w_, void* user_data); static void octave_callback(void *w_, void* user_data) noexcept; static void keymap_callback(void *w_, void* user_data); @@ -267,21 +213,6 @@ class XKeyBoard { static void through_callback(void *w_, void* user_data); static void midi_map_callback(void *w_, void* user_data); static void synth_callback(void *w_, void* user_data); - static void modwheel_callback(void *w_, void* user_data) noexcept; - static void detune_callback(void *w_, void* user_data) noexcept; - static void attack_callback(void *w_, void* user_data) noexcept; - static void expression_callback(void *w_, void* user_data) noexcept; - static void release_callback(void *w_, void* user_data) noexcept; - static void cutoff_callback(void *w_, void* user_data) noexcept; - static void resonance_callback(void *w_, void* user_data) noexcept; - static void volume_callback(void *w_, void* user_data) noexcept; - static void velocity_callback(void *w_, void* user_data) noexcept; - static void pitchwheel_callback(void *w_, void* user_data) noexcept; - static void balance_callback(void *w_, void* user_data) noexcept; - static void pitchwheel_release_callback(void *w_, void* button, void* user_data) noexcept; - static void pitchwheel_press_callback(void *w_, void* button, void* user_data) noexcept; - static void sustain_callback(void *w_, void* user_data) noexcept; - static void sostenuto_callback(void *w_, void* user_data) noexcept; static void record_callback(void *w_, void* user_data); static void play_callback(void *w_, void* user_data) noexcept; static void pause_callback(void *w_, void* user_data) noexcept; @@ -372,7 +303,7 @@ class XKeyBoard { XKeyBoard(xjack::XJack *xjack, xalsa::XAlsa *xalsa, xsynth::XSynth *xsynth, midimapper::MidiMapper *midimap, mamba::MidiMessenger *mmessage, nsmhandler::NsmSignalHandler& nsmsig, - PosixSignalHandler& xsig, AnimatedKeyBoard * animidi); + signalhandler::PosixSignalHandler& xsig, animatedkeyboard::AnimatedKeyBoard * animidi); ~XKeyBoard(); xjack::XJack *xjack; diff --git a/src/PosixSignalHandler.cpp b/src/PosixSignalHandler.cpp new file mode 100644 index 0000000..7617674 --- /dev/null +++ b/src/PosixSignalHandler.cpp @@ -0,0 +1,103 @@ +/* + * 0BSD + * + * BSD Zero Clause License + * + * Copyright (c) 2020 Hermann Meyer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include +#include + +#include + +#include "PosixSignalHandler.h" + +/**************************************************************** + ** class PosixSignalHandler + ** + ** Watch for incoming system signals in a extra thread + ** + */ + +namespace signalhandler { + +PosixSignalHandler::PosixSignalHandler() + : sigc::trackable(), + waitset(), + thread(nullptr), + exit(false) { + sigemptyset(&waitset); + + sigaddset(&waitset, SIGINT); + sigaddset(&waitset, SIGQUIT); + sigaddset(&waitset, SIGTERM); + sigaddset(&waitset, SIGHUP); + sigaddset(&waitset, SIGKILL); + + sigprocmask(SIG_BLOCK, &waitset, NULL); + create_thread(); +} + +PosixSignalHandler::~PosixSignalHandler() { + if (thread) { + exit = true; + pthread_kill(thread->native_handle(), SIGINT); + thread->join(); + delete thread; + } + sigprocmask(SIG_UNBLOCK, &waitset, NULL); +} + +void PosixSignalHandler::create_thread() { + try { + thread = new std::thread( + sigc::mem_fun(*this, &PosixSignalHandler::signal_helper_thread)); + } catch (std::system_error& e) { + fprintf(stderr,"Thread create failed (signal): %s", e.what()); + } +} + +void PosixSignalHandler::signal_helper_thread() { + + pthread_sigmask(SIG_BLOCK, &waitset, NULL); + while (true) { + int sig; + int ret = sigwait(&waitset, &sig); + if (exit) { + break; + } + if (ret != 0) { + assert(errno == EINTR); + continue; + } + switch (sig) { + case SIGINT: + case SIGTERM: + case SIGQUIT: + trigger_quit_by_posix(sig); + break; + case SIGHUP: + case SIGKILL: + trigger_kill_by_posix(sig); + break; + default: + break; + } + } +} + +} // namespace signalhandler diff --git a/src/PosixSignalHandler.h b/src/PosixSignalHandler.h new file mode 100644 index 0000000..ca0eabb --- /dev/null +++ b/src/PosixSignalHandler.h @@ -0,0 +1,59 @@ +/* + * 0BSD + * + * BSD Zero Clause License + * + * Copyright (c) 2020 Hermann Meyer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include + +#pragma once + +#ifndef POSIXSIGNALHANDLER_H +#define POSIXSIGNALHANDLER_H + +/**************************************************************** + ** class PosixSignalHandler + ** + ** Watch for incoming system signals in a extra thread + ** + */ + +namespace signalhandler { + +class PosixSignalHandler : public sigc::trackable { +private: + sigset_t waitset; + std::thread *thread; + volatile bool exit; + void signal_helper_thread(); + void create_thread(); + +public: + PosixSignalHandler(); + ~PosixSignalHandler(); + + sigc::signal trigger_quit_by_posix; + sigc::signal& signal_trigger_quit_by_posix() { return trigger_quit_by_posix; } + + sigc::signal trigger_kill_by_posix; + sigc::signal& signal_trigger_kill_by_posix() { return trigger_kill_by_posix; } +}; + +} // namespace signalhandler + +#endif // POSIXSIGNALHANDLER_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..7096f5d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,166 @@ +/* + * 0BSD + * + * BSD Zero Clause License + * + * Copyright (c) 2020 Hermann Meyer + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "MidiKeyBoard.h" +#include "xmkeyboard.h" + + +/**************************************************************** + ** main + ** + ** init the classes, create the UI, open jackd-client and start application + ** + */ + +int main (int argc, char *argv[]) { + auto t1 = std::chrono::high_resolution_clock::now(); + +#ifdef ENABLE_NLS + // set Message type to locale to fetch localisation support + std::setlocale (LC_MESSAGES, ""); + // set Ctype to C to avoid symbol clashes from different locales + std::setlocale (LC_CTYPE, "C"); + bindtextdomain(GETTEXT_PACKAGE, LOCAL_DIR); + bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + textdomain(GETTEXT_PACKAGE); +#endif + + if(0 == XInitThreads()) + fprintf(stderr, "Warning: XInitThreads() failed\n"); + + signalhandler::PosixSignalHandler xsig; + + Xputty app; + + mamba::MidiMessenger mmessage; + nsmhandler::NsmSignalHandler nsmsig; + animatedkeyboard::AnimatedKeyBoard animidi; + + midimapper::MidiMapper midimap([&mmessage] + (int _cc, int _pg, int _bgn, int _num, bool have_channel) noexcept + {mmessage.send_midi_cc( _cc, _pg, _bgn, _num, have_channel);}); + + xalsa::XAlsa xalsa([&mmessage] + (int _cc, int _pg, int _bgn, int _num, bool have_channel) noexcept + {mmessage.send_midi_cc( _cc, _pg, _bgn, _num, have_channel);}, + [&midimap] (const uint8_t* m ,uint8_t n ) noexcept {midimap.mmapper_input_notify(m,n);}); + + xjack::XJack xjack(&mmessage, + [&xalsa] (const uint8_t* m ,uint8_t n ) noexcept {xalsa.xalsa_output_notify(m,n);}, + [&xalsa] (int p ) {xalsa.xalsa_set_priority(p);}, + [&midimap] (const uint8_t* m ,uint8_t n ) noexcept {midimap.mmapper_input_notify(m,n);}, + [&midimap] (int p ) {midimap.mmapper_set_priority(p);}); + + xsynth::XSynth xsynth; + midikeyboard::XKeyBoard xjmkb(&xjack, &xalsa, &xsynth, &midimap, &mmessage, nsmsig, xsig, &animidi); + nsmhandler::NsmHandler nsmh(&nsmsig); + + nsmsig.nsm_session_control = nsmh.check_nsm(xjmkb.client_name.c_str(), argv); + + main_init(&app); + + if (xjack.init_jack()) { + + if (!nsmsig.nsm_session_control) + xjmkb.set_config_file(); + + xjmkb.read_config(); + xjmkb.init_ui(&app); + MambaKeyboard *keys = (MambaKeyboard*)xjmkb.wid->parent_struct; + midimap.mmapper_start([keys] (int channel, int key, bool set) + {mamba_set_key_in_matrix(keys->in_key_matrix[channel], key, set);}); + if (xalsa.xalsa_init(xjack.client_name.c_str(), "input", "output") >= 0) { + xalsa.xalsa_start([keys] (int channel, int key, bool set) + {mamba_set_key_in_matrix(keys->in_key_matrix[channel], key, set);}); + } else { + fprintf(stderr, _("Couldn't open a alsa port, is the alsa sequencer running?\n")); + } + + if (!xjmkb.soundfont.empty()) { + std::string synth_instance = xjack.client_name; + std::transform(synth_instance.begin(), synth_instance.end(), synth_instance.begin(), ::tolower); + xsynth.setup(xjack.SampleRate, synth_instance.c_str()); + xsynth.init_synth(); + xsynth.load_soundfont(xjmkb.soundfont.c_str()); + const char **port_list = NULL; + port_list = jack_get_ports(xjack.client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput); + if (port_list) { + for (int i = 0; port_list[i] != NULL; i++) { + if (strstr(port_list[i], synth_instance.c_str())) { + const char *my_port = jack_port_name(xjack.out_port); + jack_connect(xjack.client, my_port,port_list[i]); + break; + } + } + jack_free(port_list); + port_list = NULL; + } + for (int i = 0; i<16;i++) + mmessage.send_midi_cc(0xB0 | i, 7, xjmkb.volume[i], 3, true); + xjmkb.fs[0]->state = 0; + xjmkb.fs[1]->state = 0; + xjmkb.fs[2]->state = 0; + xjmkb.fs[3]->state = 0; + xjmkb.rebuild_instrument_list(); + xjmkb.rebuild_soundfont_list(); + xjmkb.init_modulators(&xjmkb); + } + + if (argc > 1) { + +#ifdef __XDG_MIME_H__ + if(strstr(xdg_mime_get_mime_type_from_file_name(argv[1]), "midi")) { +#else + if( access(argv[1], F_OK ) != -1 ) { +#endif + xjmkb.dialog_load_response(xjmkb.win, (void*) &argv[1]); + } + } + + if (xsynth.synth_is_active()) { + for(std::vector::iterator i = xjack.rec.play[0].begin(); i != xjack.rec.play[0].end(); ++i) { + if (((*i).buffer[0] & 0xf0) == 0xB0 && ((*i).buffer[1]== 32 || (*i).buffer[1]== 0)) { + mmessage.send_midi_cc((*i).buffer[0], (*i).buffer[1], (*i).buffer[2], 3, true); + } else if (((*i).buffer[0] & 0xf0) == 0xC0 ) { + mmessage.send_midi_cc((*i).buffer[0], (*i).buffer[1], 0, 2, true); + } + } + } + xjmkb.client_name = xjack.client_name; + std::string tittle = xjmkb.client_name + _(" - Virtual Midi Keyboard"); + widget_set_title(xjmkb.win, tittle.c_str()); + xjmkb.show_ui(xjmkb.visible); + if (xsynth.synth_is_active()) xjmkb.rebuild_instrument_list(); + auto t2 = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast( t2 - t1 ).count(); + debug_print("%f sec\n",duration/1e+6); + + main_run(&app); + + animidi.stop(); + if (xjack.client) jack_client_close (xjack.client); + xsynth.unload_synth(); + if(!nsmsig.nsm_session_control) xjmkb.save_config(); + } + main_quit(&app); + + exit (0); + +}