diff --git a/README.md b/README.md index 747b532..301d7fd 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,10 @@ The commands supported by mod-host are: * unmap the MIDI controller from a control port e.g.: midi_unmap 0 "gain" + monitor_audio_levels + * monitor audio levels for a specific jack port (on the feedback port) + e.g.: monitor_audio_levels "system:capture_1" 1 + monitor_midi_program * listen to MIDI program change messages (on the feedback port) e.g.: monitor_midi_program 0 1 diff --git a/src/effects.c b/src/effects.c index 14e0a0e..093ac1c 100644 --- a/src/effects.c +++ b/src/effects.c @@ -277,6 +277,7 @@ enum { enum PostPonedEventType { POSTPONED_PARAM_SET, + POSTPONED_AUDIO_MONITOR, POSTPONED_OUTPUT_MONITOR, POSTPONED_MIDI_PROGRAM_CHANGE, POSTPONED_MIDI_MAP, @@ -305,6 +306,12 @@ typedef struct HMI_ADDRESSING_T hmi_addressing_t; #endif typedef struct PORT_T port_t; +typedef struct AUDIO_MONITOR_T { + jack_port_t *port; + char *source_port_name; + float value; +} audio_monitor_t; + typedef struct CV_SOURCE_T { port_t *port; jack_port_t *jack_port; @@ -586,6 +593,11 @@ typedef struct POSTPONED_PARAMETER_EVENT_T { float value; } postponed_parameter_event_t; +typedef struct POSTPONED_AUDIO_MONITOR_EVENT_T { + int index; + float value; +} postponed_audio_monitor_event_t; + typedef struct POSTPONED_MIDI_PROGRAM_CHANGE_EVENT_T { int8_t program; int8_t channel; @@ -628,6 +640,7 @@ typedef struct POSTPONED_EVENT_T { enum PostPonedEventType type; union { postponed_parameter_event_t parameter; + postponed_audio_monitor_event_t audio_monitor; postponed_midi_program_change_event_t program_change; postponed_midi_map_event_t midi_map; postponed_transport_event_t transport; @@ -725,6 +738,9 @@ static jack_port_t *g_audio_in2_port; static jack_port_t *g_audio_out1_port; static jack_port_t *g_audio_out2_port; #endif +static audio_monitor_t *g_audio_monitors; +static pthread_mutex_t g_audio_monitor_mutex; +static int g_audio_monitor_count; static jack_port_t *g_midi_in_port; static jack_position_t g_jack_pos; static bool g_jack_rolling; @@ -1226,16 +1242,18 @@ static void RunPostPonedEvents(int ignored_effect_id) // cached data, to make sure we only handle similar events once bool got_midi_program = false; bool got_transport = false; - postponed_cached_effect_events cached_process_out_buf; + postponed_cached_effect_events cached_audio_monitor, cached_process_out_buf; postponed_cached_symbol_events cached_param_set, cached_output_mon; + cached_audio_monitor.last_effect_id = -1; cached_process_out_buf.last_effect_id = -1; cached_param_set.last_effect_id = -1; - cached_output_mon.last_effect_id = -1; cached_param_set.last_symbol[0] = '\0'; cached_param_set.last_symbol[MAX_CHAR_BUF_SIZE] = '\0'; + cached_output_mon.last_effect_id = -1; cached_output_mon.last_symbol[0] = '\0'; cached_output_mon.last_symbol[MAX_CHAR_BUF_SIZE] = '\0'; + INIT_LIST_HEAD(&cached_audio_monitor.effects.siblings); INIT_LIST_HEAD(&cached_process_out_buf.effects.siblings); INIT_LIST_HEAD(&cached_param_set.symbols.siblings); INIT_LIST_HEAD(&cached_output_mon.symbols.siblings); @@ -1284,6 +1302,23 @@ static void RunPostPonedEvents(int ignored_effect_id) strncpy(cached_param_set.last_symbol, eventptr->event.parameter.symbol, MAX_CHAR_BUF_SIZE); break; + case POSTPONED_AUDIO_MONITOR: + if (ShouldIgnorePostPonedEffectEvent(eventptr->event.audio_monitor.index, &cached_audio_monitor)) + continue; + + pthread_mutex_lock(&g_audio_monitor_mutex); + if (eventptr->event.audio_monitor.index < g_audio_monitor_count) + g_audio_monitors[eventptr->event.audio_monitor.index].value = 0.f; + pthread_mutex_unlock(&g_audio_monitor_mutex); + + snprintf(buf, FEEDBACK_BUF_SIZE, "audio_monitor %i %f", eventptr->event.audio_monitor.index, + eventptr->event.audio_monitor.value); + socket_send_feedback_debug(buf); + + // save for fast checkup next time + cached_audio_monitor.last_effect_id = eventptr->event.audio_monitor.index; + break; + case POSTPONED_OUTPUT_MONITOR: if (eventptr->event.parameter.effect_id == ignored_effect_id) continue; @@ -1553,6 +1588,11 @@ static void RunPostPonedEvents(int ignored_effect_id) postponed_cached_effect_list_data *peffect; postponed_cached_symbol_list_data *psymbol; + list_for_each_safe(it, it2, &cached_audio_monitor.effects.siblings) + { + peffect = list_entry(it, postponed_cached_effect_list_data, siblings); + free(peffect); + } list_for_each_safe(it, it2, &cached_process_out_buf.effects.siblings) { peffect = list_entry(it, postponed_cached_effect_list_data, siblings); @@ -2790,6 +2830,49 @@ static int ProcessGlobalClient(jack_nframes_t nframes, void *arg) } #endif + // Handle audio monitors + if (pthread_mutex_trylock(&g_audio_monitor_mutex) == 0) + { + float *monitorbuf; + float absvalue, oldvalue; + + for (int i = 0; i < g_audio_monitor_count; ++i) + { + monitorbuf = (float*)jack_port_get_buffer(g_audio_monitors[i].port, nframes); + oldvalue = value = g_audio_monitors[i].value; + + for (jack_nframes_t i = 0 ; i < nframes; i++) + { + absvalue = fabsf(monitorbuf[i]); + + if (absvalue > value) + value = absvalue; + } + + if (oldvalue < value) + { + g_audio_monitors[i].value = value; + + postponed_event_list_data* const posteventptr = rtsafe_memory_pool_allocate_atomic(g_rtsafe_mem_pool); + + if (posteventptr) + { + posteventptr->event.type = POSTPONED_AUDIO_MONITOR; + posteventptr->event.audio_monitor.index = i; + posteventptr->event.audio_monitor.value = value; + + pthread_mutex_lock(&g_rtsafe_mutex); + list_add_tail(&posteventptr->siblings, &g_rtsafe_list); + pthread_mutex_unlock(&g_rtsafe_mutex); + + needs_post = true; + } + } + } + + pthread_mutex_unlock(&g_audio_monitor_mutex); + } + if (UpdateGlobalJackPosition(pos_flag, false)) needs_post = true; @@ -3958,6 +4041,7 @@ int effects_init(void* client) pthread_mutex_init(&g_rtsafe_mutex, &mutex_atts); pthread_mutex_init(&g_raw_midi_port_mutex, &mutex_atts); + pthread_mutex_init(&g_audio_monitor_mutex, &mutex_atts); pthread_mutex_init(&g_midi_learning_mutex, &mutex_atts); #ifdef __MOD_DEVICES__ pthread_mutex_init(&g_hmi_mutex, &mutex_atts); @@ -4533,6 +4617,7 @@ int effects_finish(int close_client) sem_destroy(&g_postevents_semaphore); pthread_mutex_destroy(&g_rtsafe_mutex); pthread_mutex_destroy(&g_raw_midi_port_mutex); + pthread_mutex_destroy(&g_audio_monitor_mutex); pthread_mutex_destroy(&g_midi_learning_mutex); #ifdef __MOD_DEVICES__ pthread_mutex_destroy(&g_hmi_mutex); @@ -8255,6 +8340,76 @@ int effects_processing_enable(int enable) return SUCCESS; } +int effects_monitor_audio_levels(const char *source_port_name, int enable) +{ + if (g_jack_global_client == NULL) + return ERR_INVALID_OPERATION; + + if (enable) + { + for (int i = 0; i < g_audio_monitor_count; ++i) + { + if (!strcmp(g_audio_monitors[i].source_port_name, source_port_name)) + return SUCCESS; + } + + pthread_mutex_lock(&g_audio_monitor_mutex); + g_audio_monitors = realloc(g_audio_monitors, sizeof(audio_monitor_t) * (g_audio_monitor_count + 1)); + pthread_mutex_unlock(&g_audio_monitor_mutex); + + if (g_audio_monitors == NULL) + return ERR_MEMORY_ALLOCATION; + + audio_monitor_t *monitor = &g_audio_monitors[g_audio_monitor_count]; + + char port_name[0xff]; + snprintf(port_name, sizeof(port_name) - 1, "monitor_%d", g_audio_monitor_count + 1); + + jack_port_t *port = jack_port_register(g_jack_global_client, + port_name, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, + 0); + if (port == NULL) + return ERR_JACK_PORT_REGISTER; + + snprintf(port_name, sizeof(port_name) - 1, "%s:monitor_%d", + jack_get_client_name(g_jack_global_client), g_audio_monitor_count + 1); + jack_connect(g_jack_global_client, source_port_name, port_name); + + monitor->port = port; + monitor->source_port_name = strdup(source_port_name); + monitor->value = 0.f; + + ++g_audio_monitor_count; + } + else + { + if (g_audio_monitor_count == 0) + return ERR_INVALID_OPERATION; + + audio_monitor_t *monitor = &g_audio_monitors[g_audio_monitor_count - 1]; + + if (strcmp(monitor->source_port_name, source_port_name)) + return ERR_INVALID_OPERATION; + + pthread_mutex_lock(&g_audio_monitor_mutex); + --g_audio_monitor_count; + pthread_mutex_unlock(&g_audio_monitor_mutex); + + jack_port_unregister(g_jack_global_client, monitor->port); + free(monitor->source_port_name); + + if (g_audio_monitor_count == 0) + { + free(g_audio_monitors); + g_audio_monitors = NULL; + } + } + + return SUCCESS; +} + int effects_monitor_midi_program(int channel, int enable) { if (channel < 0 || channel > 15) diff --git a/src/effects.h b/src/effects.h index 36cc2a7..9e304cf 100644 --- a/src/effects.h +++ b/src/effects.h @@ -183,6 +183,7 @@ int effects_state_set_tmpdir(const char *dir); int effects_aggregated_midi_enable(int enable); int effects_freewheeling_enable(int enable); int effects_processing_enable(int enable); +int effects_monitor_audio_levels(const char *source_port_name, int enable); int effects_monitor_midi_program(int channel, int enable); void effects_transport(int rolling, double beats_per_bar, double beats_per_minute); int effects_transport_sync_mode(const char *mode); diff --git a/src/mod-host.c b/src/mod-host.c index ed1972c..bdb44ca 100644 --- a/src/mod-host.c +++ b/src/mod-host.c @@ -318,6 +318,13 @@ static void monitor_output_cb(proto_t *proto) protocol_response_int(resp, proto); } +static void monitor_audio_levels_cb(proto_t *proto) +{ + int resp; + resp = effects_monitor_audio_levels(proto->list[1], atoi(proto->list[2])); + protocol_response_int(resp, proto); +} + static void monitor_midi_program_cb(proto_t *proto) { int resp; @@ -380,6 +387,7 @@ static void cc_map_cb(proto_t *proto) } else { + scalepoints_count = 0; scalepoints = NULL; } @@ -714,6 +722,7 @@ static int mod_host_init(jack_client_t* client, int socket_port, int feedback_po protocol_add_command(EFFECT_SET_BPB, effects_set_beats_per_bar_cb); protocol_add_command(MONITOR_ADDR_SET, monitor_addr_set_cb); protocol_add_command(MONITOR_OUTPUT, monitor_output_cb); + protocol_add_command(MONITOR_AUDIO_LEVELS, monitor_audio_levels_cb); protocol_add_command(MONITOR_MIDI_PROGRAM, monitor_midi_program_cb); protocol_add_command(MIDI_LEARN, midi_learn_cb); protocol_add_command(MIDI_MAP, midi_map_cb); diff --git a/src/mod-host.h b/src/mod-host.h index 56da20c..0fcedee 100644 --- a/src/mod-host.h +++ b/src/mod-host.h @@ -72,6 +72,7 @@ #define EFFECT_SET_BPB "set_bpb %f" #define MONITOR_ADDR_SET "monitor %s %i %i" #define MONITOR_OUTPUT "monitor_output %i %s" +#define MONITOR_AUDIO_LEVELS "monitor_audio_levels %i %s" #define MONITOR_MIDI_PROGRAM "monitor_midi_program %i %i" #define MIDI_LEARN "midi_learn %i %s %f %f" #define MIDI_MAP "midi_map %i %s %i %i %f %f"