]> git.sesse.net Git - nageru/blobdiff - midi_mapper.cpp
Support audio-only FFmpeg inputs. Somewhat wonky, though.
[nageru] / midi_mapper.cpp
index 6395ff7381a25a008214188449e2f4c22ed53a6c..3b22192b3b8b0670356ebdb9230a8133777b0983 100644 (file)
@@ -1,18 +1,26 @@
 #include "midi_mapper.h"
 
-#include "audio_mixer.h"
-#include "midi_mapping.pb.h"
-
 #include <alsa/asoundlib.h>
-#include <google/protobuf/text_format.h>
-#include <google/protobuf/io/zero_copy_stream.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <assert.h>
+#include <errno.h>
 #include <fcntl.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/text_format.h>
+#include <pthread.h>
+#include <poll.h>
+#include <stdint.h>
+#include <stdio.h>
 #include <sys/eventfd.h>
-
+#include <unistd.h>
+#include <algorithm>
 #include <functional>
 #include <thread>
 
+#include "audio_mixer.h"
+#include "midi_mapping.pb.h"
+
 using namespace google::protobuf;
 using namespace std;
 using namespace std::placeholders;
@@ -38,14 +46,16 @@ MIDIMapper::MIDIMapper(ControllerReceiver *receiver)
 {
        should_quit_fd = eventfd(/*initval=*/0, /*flags=*/0);
        assert(should_quit_fd != -1);
-       refresh_highlights();
 }
 
 MIDIMapper::~MIDIMapper()
 {
        should_quit = true;
        const uint64_t one = 1;
-       write(should_quit_fd, &one, sizeof(one));
+       if (write(should_quit_fd, &one, sizeof(one)) != sizeof(one)) {
+               perror("write(should_quit_fd)");
+               exit(1);
+       }
        midi_thread.join();
        close(should_quit_fd);
 }
@@ -139,6 +149,8 @@ ControllerReceiver *MIDIMapper::set_receiver(ControllerReceiver *new_receiver)
 
 void MIDIMapper::thread_func()
 {
+       pthread_setname_np(pthread_self(), "MIDIMapper");
+
        snd_seq_t *seq;
        int err;
 
@@ -220,6 +232,10 @@ void MIDIMapper::thread_func()
                        if (err < 0) {
                                if (err == -EINTR) continue;
                                if (err == -EAGAIN) break;
+                               if (err == -ENOSPC) {
+                                       fprintf(stderr, "snd_seq_event_input: Some events were lost.\n");
+                                       continue;
+                               }
                                fprintf(stderr, "snd_seq_event_input: %s\n", snd_strerror(err));
                                return;
                        }
@@ -240,8 +256,6 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
        lock_guard<mutex> lock(mu);
        switch (event->type) {
        case SND_SEQ_EVENT_CONTROLLER: {
-               printf("Controller %d changed to %d\n", event->data.control.param, event->data.control.value);
-
                const int controller = event->data.control.param;
                const float value = map_controller_to_float(event->data.control.value);
 
@@ -256,6 +270,8 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
                        value, bind(&ControllerReceiver::set_makeup_gain, receiver, _2));
 
                // Bus controllers.
+               match_controller(controller, MIDIMappingBusProto::kStereoWidthFieldNumber, MIDIMappingProto::kStereoWidthBankFieldNumber,
+                       value, bind(&ControllerReceiver::set_stereo_width, receiver, _1, _2));
                match_controller(controller, MIDIMappingBusProto::kTrebleFieldNumber, MIDIMappingProto::kTrebleBankFieldNumber,
                        value, bind(&ControllerReceiver::set_treble, receiver, _1, _2));
                match_controller(controller, MIDIMappingBusProto::kMidFieldNumber, MIDIMappingProto::kMidBankFieldNumber,
@@ -275,8 +291,6 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
 
                receiver->note_on(note);
 
-               printf("Note: %d\n", note);
-
                for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
                        const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
                        if (bus_mapping.has_prev_bank() &&
@@ -335,10 +349,13 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
                        bind(&ControllerReceiver::toggle_compressor, receiver, _1));
                match_button(note, MIDIMappingBusProto::kClearPeakFieldNumber, MIDIMappingProto::kClearPeakBankFieldNumber,
                        bind(&ControllerReceiver::clear_peak, receiver, _1));
+               match_button(note, MIDIMappingBusProto::kToggleMuteFieldNumber, MIDIMappingProto::kClearPeakBankFieldNumber,
+                       bind(&ControllerReceiver::toggle_mute, receiver, _1));
                match_button(note, MIDIMappingBusProto::kToggleLimiterFieldNumber, MIDIMappingProto::kToggleLimiterBankFieldNumber,
                        bind(&ControllerReceiver::toggle_limiter, receiver));
                match_button(note, MIDIMappingBusProto::kToggleAutoMakeupGainFieldNumber, MIDIMappingProto::kToggleAutoMakeupGainBankFieldNumber,
                        bind(&ControllerReceiver::toggle_auto_makeup_gain, receiver));
+               break;
        }
        case SND_SEQ_EVENT_PORT_START:
                subscribe_to_port_lock_held(seq, event->data.addr);
@@ -346,13 +363,27 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
        case SND_SEQ_EVENT_PORT_EXIT:
                printf("MIDI port %d:%d went away.\n", event->data.addr.client, event->data.addr.port);
                break;
+       case SND_SEQ_EVENT_PORT_SUBSCRIBED:
+               if (event->data.connect.sender.client != 0 &&  // Ignore system senders.
+                   event->data.connect.sender.client != snd_seq_client_id(seq) &&
+                   event->data.connect.dest.client == snd_seq_client_id(seq)) {
+                       ++num_subscribed_ports;
+                       update_highlights();
+               }
+               break;
+       case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
+               if (event->data.connect.sender.client != 0 &&  // Ignore system senders.
+                   event->data.connect.sender.client != snd_seq_client_id(seq) &&
+                   event->data.connect.dest.client == snd_seq_client_id(seq)) {
+                       --num_subscribed_ports;
+                       update_highlights();
+               }
+               break;
        case SND_SEQ_EVENT_NOTEOFF:
        case SND_SEQ_EVENT_CLIENT_START:
        case SND_SEQ_EVENT_CLIENT_EXIT:
        case SND_SEQ_EVENT_CLIENT_CHANGE:
        case SND_SEQ_EVENT_PORT_CHANGE:
-       case SND_SEQ_EVENT_PORT_SUBSCRIBED:
-       case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
                break;
        default:
                printf("Ignoring MIDI event of unknown type %d.\n", event->type);
@@ -361,8 +392,14 @@ void MIDIMapper::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
 
 void MIDIMapper::subscribe_to_port_lock_held(snd_seq_t *seq, const snd_seq_addr_t &addr)
 {
-       // Client 0 is basically the system; ignore it.
-       if (addr.client == 0) {
+       // Client 0 (SNDRV_SEQ_CLIENT_SYSTEM) is basically the system; ignore it.
+       // MIDI through (SNDRV_SEQ_CLIENT_DUMMY) echoes back what we give it, so ignore that, too.
+       if (addr.client == 0 || addr.client == 14) {
+               return;
+       }
+
+       // Don't listen to ourselves.
+       if (addr.client == snd_seq_client_id(seq)) {
                return;
        }
 
@@ -467,6 +504,11 @@ void MIDIMapper::refresh_lights()
 
 void MIDIMapper::update_highlights()
 {
+       if (num_subscribed_ports.load() == 0) {
+               receiver->clear_all_highlights();
+               return;
+       }
+
        // Global controllers.
        bool highlight_locut = false;
        bool highlight_limiter_threshold = false;
@@ -503,6 +545,8 @@ void MIDIMapper::update_highlights()
 
        // Per-bus controllers.
        for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
+               receiver->highlight_stereo_width(bus_idx, has_active_controller(
+                       bus_idx, MIDIMappingBusProto::kStereoWidthFieldNumber, MIDIMappingProto::kStereoWidthBankFieldNumber));
                receiver->highlight_treble(bus_idx, has_active_controller(
                        bus_idx, MIDIMappingBusProto::kTrebleFieldNumber, MIDIMappingProto::kTrebleBankFieldNumber));
                receiver->highlight_mid(bus_idx, has_active_controller(
@@ -515,6 +559,8 @@ void MIDIMapper::update_highlights()
                        bus_idx, MIDIMappingBusProto::kCompressorThresholdFieldNumber, MIDIMappingProto::kCompressorThresholdBankFieldNumber));
                receiver->highlight_fader(bus_idx, has_active_controller(
                        bus_idx, MIDIMappingBusProto::kFaderFieldNumber, MIDIMappingProto::kFaderBankFieldNumber));
+               receiver->highlight_mute(bus_idx, has_active_controller(
+                       bus_idx, MIDIMappingBusProto::kToggleMuteFieldNumber, MIDIMappingProto::kToggleMuteBankFieldNumber));
                receiver->highlight_toggle_locut(bus_idx, has_active_controller(
                        bus_idx, MIDIMappingBusProto::kToggleLocutFieldNumber, MIDIMappingProto::kToggleLocutBankFieldNumber));
                receiver->highlight_toggle_auto_gain_staging(bus_idx, has_active_controller(
@@ -554,6 +600,9 @@ void MIDIMapper::update_lights_lock_held()
        }
        unsigned num_buses = min<unsigned>(global_audio_mixer->num_buses(), mapping_proto->bus_mapping_size());
        for (unsigned bus_idx = 0; bus_idx < num_buses; ++bus_idx) {
+               if (global_audio_mixer->get_mute(bus_idx)) {
+                       activate_lights(bus_idx, MIDIMappingBusProto::kIsMutedFieldNumber, &active_lights);
+               }
                if (global_audio_mixer->get_locut_enabled(bus_idx)) {
                        activate_lights(bus_idx, MIDIMappingBusProto::kLocutIsOnFieldNumber, &active_lights);
                }