/* * JACK Rack * * Original: * Copyright (C) Robert Ham 2002, 2003 (node@users.sourceforge.net) * * Modification for MLT: * Copyright (C) 2004-2021 Meltytech, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #ifdef WITH_JACK #include #endif #include #include #include #include #include #include #include #include "process.h" #include "lock_free_fifo.h" #include "plugin.h" #include "jack_rack.h" #include "framework/mlt_log.h" #ifndef _ #define _(x) x #endif extern pthread_mutex_t g_activate_mutex; #define USEC_PER_SEC 1000000 #define MSEC_PER_SEC 1000 #define TIME_RUN_SKIP_COUNT 5 #define MAX_BUFFER_SIZE 4096 jack_nframes_t sample_rate; jack_nframes_t buffer_size; #ifdef WITH_JACK static void jack_shutdown_cb (void * data) { process_info_t * procinfo = data; procinfo->quit = TRUE; } #endif /** process messages for plugins' control ports */ void process_control_port_messages (process_info_t * procinfo) { plugin_t * plugin; unsigned long control; unsigned long channel; gint copy; if (!procinfo->chain) return; for (plugin = procinfo->chain; plugin; plugin = plugin->next) { if (plugin->desc->control_port_count > 0) for (control = 0; control < plugin->desc->control_port_count; control++) for (copy = 0; copy < plugin->copies; copy++) { while (lff_read (plugin->holders[copy].ui_control_fifos + control, plugin->holders[copy].control_memory + control) == 0); } if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) { while (lff_read (plugin->wet_dry_fifos + channel, plugin->wet_dry_values + channel) == 0); } } } #ifdef WITH_JACK static int get_jack_buffers (process_info_t * procinfo, jack_nframes_t frames) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { procinfo->jack_input_buffers[channel] = jack_port_get_buffer (procinfo->jack_input_ports[channel], frames); if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } procinfo->jack_output_buffers[channel] = jack_port_get_buffer (procinfo->jack_output_ports[channel], frames); if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } return 0; } #endif plugin_t * get_first_enabled_plugin (process_info_t * procinfo) { plugin_t * first_enabled; if (!procinfo->chain) return NULL; for (first_enabled = procinfo->chain; first_enabled; first_enabled = first_enabled->next) { if (first_enabled->enabled) return first_enabled; } return NULL; } plugin_t * get_last_enabled_plugin (process_info_t * procinfo) { plugin_t * last_enabled; if (!procinfo->chain) return NULL; for (last_enabled = procinfo->chain_end; last_enabled; last_enabled = last_enabled->prev) { if (last_enabled->enabled) return last_enabled; } return NULL; } void connect_chain (process_info_t * procinfo, jack_nframes_t frames) { plugin_t * first_enabled, * last_enabled, * plugin; gint copy; unsigned long channel; if (!procinfo->chain) return; first_enabled = get_first_enabled_plugin (procinfo); if (!first_enabled) return; last_enabled = get_last_enabled_plugin (procinfo); /* sort out the aux ports */ plugin = first_enabled; do { if (plugin->desc->aux_channels > 0 && plugin->enabled) { #ifdef WITH_JACK if (procinfo->jack_client) { for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames)); } else #endif { for (copy = 0; copy < frames; copy++) procinfo->silent_buffer[copy] = 0.0; for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) plugin->descriptor-> connect_port (plugin->holders[copy].instance, plugin->desc->audio_aux_port_indicies[channel], procinfo->silent_buffer); } } } while ( (plugin != last_enabled) && (plugin = plugin->next) ); /* ensure that all the of the enabled plugins are connected to their memory */ plugin_connect_output_ports (first_enabled); if (first_enabled != last_enabled) { plugin_connect_input_ports (last_enabled, last_enabled->prev->audio_output_memory); for (plugin = first_enabled->next; plugin; plugin = plugin->next) { if (plugin->enabled) { plugin_connect_input_ports (plugin, plugin->prev->audio_output_memory); plugin_connect_output_ports (plugin); } } } /* input buffers for first plugin */ if( plugin->desc->has_input ) plugin_connect_input_ports (first_enabled, procinfo->jack_input_buffers); } void process_chain (process_info_t * procinfo, jack_nframes_t frames) { plugin_t * first_enabled; plugin_t * last_enabled = NULL; plugin_t * plugin; unsigned long channel; unsigned long i; #ifdef WITH_JACK if (procinfo->jack_client) { LADSPA_Data zero_signal[frames]; guint copy; /* set the zero signal to zero */ for (channel = 0; channel < frames; channel++) zero_signal[channel] = 0.0; /* possibly set aux output channels to zero if they're not enabled */ for (plugin = procinfo->chain; plugin; plugin = plugin->next) if (!plugin->enabled && plugin->desc->aux_channels > 0 && !plugin->desc->aux_are_input) for (copy = 0; copy < plugin->copies; copy++) for (channel = 0; channel < plugin->desc->aux_channels; channel++) memcpy (jack_port_get_buffer (plugin->holders[copy].aux_ports[channel], frames), zero_signal, sizeof (LADSPA_Data) * frames); } #endif first_enabled = get_first_enabled_plugin (procinfo); /* no chain; just copy input to output */ if (!procinfo->chain || !first_enabled) { unsigned long channel; for (channel = 0; channel < procinfo->channels; channel++) { memcpy (procinfo->jack_output_buffers[channel], procinfo->jack_input_buffers[channel], sizeof(LADSPA_Data) * frames); } return; } /* all past here is guaranteed to have at least 1 enabled plugin */ last_enabled = get_last_enabled_plugin (procinfo); for (plugin = first_enabled; plugin; plugin = plugin->next) { if (plugin->enabled) { for (i = 0; i < plugin->copies; i++) plugin->descriptor->run (plugin->holders[i].instance, frames); if (plugin->wet_dry_enabled) for (channel = 0; channel < procinfo->channels; channel++) for (i = 0; i < frames; i++) { plugin->audio_output_memory[channel][i] *= plugin->wet_dry_values[channel]; plugin->audio_output_memory[channel][i] += plugin->audio_input_memory[channel][i] * (1.0 - plugin->wet_dry_values[channel]); } if (plugin == last_enabled) break; } else { /* copy the data through */ for (i = 0; i < procinfo->channels; i++) memcpy (plugin->audio_output_memory[i], plugin->prev->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } } /* copy the last enabled data to the jack ports */ for (i = 0; i < procinfo->channels; i++) memcpy (procinfo->jack_output_buffers[i], last_enabled->audio_output_memory[i], sizeof(LADSPA_Data) * frames); } int process_ladspa (process_info_t * procinfo, jack_nframes_t frames, LADSPA_Data ** inputs, LADSPA_Data ** outputs) { unsigned long channel; if (!procinfo) { mlt_log_error( NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->quit == TRUE) return 1; process_control_port_messages (procinfo); for (channel = 0; channel < procinfo->channels; channel++) { if(get_first_enabled_plugin (procinfo)->desc->has_input) { procinfo->jack_input_buffers[channel] = inputs[channel]; if (!procinfo->jack_input_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for input port %ld\n", __FUNCTION__, channel); return 1; } } procinfo->jack_output_buffers[channel] = outputs[channel]; if (!procinfo->jack_output_buffers[channel]) { mlt_log_verbose( NULL, "%s: no jack buffer for output port %ld\n", __FUNCTION__, channel); return 1; } } connect_chain (procinfo, frames); process_chain (procinfo, frames); return 0; } #ifdef WITH_JACK int process_jack (jack_nframes_t frames, void * data) { int err; process_info_t * procinfo; procinfo = (process_info_t *) data; if (!procinfo) { mlt_log_error( NULL, "%s: no process_info from jack!\n", __FUNCTION__); return 1; } if (procinfo->port_count == 0) return 0; if (procinfo->quit == TRUE) return 1; process_control_port_messages (procinfo); err = get_jack_buffers (procinfo, frames); if (err) { mlt_log_warning( NULL, "%s: failed to get jack ports, not processing\n", __FUNCTION__); return 0; } connect_chain (procinfo, frames); process_chain (procinfo, frames); return 0; } /******************************************* ************** non RT stuff *************** *******************************************/ static int process_info_connect_jack (process_info_t * procinfo) { mlt_log_info( NULL, _("Connecting to JACK server with client name '%s'\n"), procinfo->jack_client_name); procinfo->jack_client = jack_client_open (procinfo->jack_client_name, JackNullOption, NULL); if (!procinfo->jack_client) { mlt_log_warning( NULL, "%s: could not create jack client; is the jackd server running?\n", __FUNCTION__); return 1; } mlt_log_verbose( NULL, _("Connected to JACK server\n")); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); return 0; } static void process_info_connect_port (process_info_t * procinfo, gshort in, unsigned long port_index, const char * port_name) { const char ** jack_ports; unsigned long jack_port_index; int err; char * full_port_name; jack_ports = jack_get_ports (procinfo->jack_client, NULL, NULL, JackPortIsPhysical | (in ? JackPortIsOutput : JackPortIsInput)); if (!jack_ports) return; for (jack_port_index = 0; jack_ports[jack_port_index] && jack_port_index <= port_index; jack_port_index++) { if (jack_port_index != port_index) continue; full_port_name = g_strdup_printf ("%s:%s", procinfo->jack_client_name, port_name); mlt_log_debug( NULL, _("Connecting ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); err = jack_connect (procinfo->jack_client, in ? jack_ports[jack_port_index] : full_port_name, in ? full_port_name : jack_ports[jack_port_index]); if (err) mlt_log_warning( NULL, "%s: error connecting ports '%s' and '%s'\n", __FUNCTION__, full_port_name, jack_ports[jack_port_index]); else mlt_log_info( NULL, _("Connected ports '%s' and '%s'\n"), full_port_name, jack_ports[jack_port_index]); free (full_port_name); } free (jack_ports); } static int process_info_set_port_count (process_info_t * procinfo, unsigned long port_count, gboolean connect_inputs, gboolean connect_outputs) { unsigned long i; char * port_name; jack_port_t ** port_ptr; gshort in; if (procinfo->port_count >= port_count) return -1; if (procinfo->port_count == 0) { procinfo->jack_input_ports = g_malloc (sizeof (jack_port_t *) * port_count); procinfo->jack_output_ports = g_malloc (sizeof (jack_port_t *) * port_count); procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * port_count); } else { procinfo->jack_input_ports = g_realloc (procinfo->jack_input_ports, sizeof (jack_port_t *) * port_count); procinfo->jack_output_ports = g_realloc (procinfo->jack_output_ports, sizeof (jack_port_t *) * port_count); procinfo->jack_input_buffers = g_realloc (procinfo->jack_input_buffers, sizeof (LADSPA_Data *) * port_count); procinfo->jack_output_buffers = g_realloc (procinfo->jack_output_buffers, sizeof (LADSPA_Data *) * port_count); } for (i = procinfo->port_count; i < port_count; i++) { for (in = 0; in < 2; in++) { port_name = g_strdup_printf ("%s_%ld", in ? "in" : "out", i + 1); //mlt_log_debug( NULL, _("Creating %s port %s\n"), in ? "input" : "output", port_name); port_ptr = (in ? &procinfo->jack_input_ports[i] : &procinfo->jack_output_ports[i]); *port_ptr = jack_port_register (procinfo->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, in ? JackPortIsInput : JackPortIsOutput, 0); if (!*port_ptr) { mlt_log_error( NULL, "%s: could not register port '%s'; aborting\n", __FUNCTION__, port_name); return 1; } //mlt_log_debug( NULL, _("Created %s port %s\n"), in ? "input" : "output", port_name); if ((in && connect_inputs) || (!in && connect_outputs)) process_info_connect_port (procinfo, in, i, port_name); g_free (port_name); } } procinfo->port_count = port_count; return 0; } #endif void process_info_set_channels (process_info_t * procinfo, unsigned long channels, gboolean connect_inputs, gboolean connect_outputs) { #ifdef WITH_JACK process_info_set_port_count (procinfo, channels, connect_inputs, connect_outputs); #endif procinfo->channels = channels; } process_info_t * process_info_new (const char * client_name, unsigned long rack_channels, gboolean connect_inputs, gboolean connect_outputs) { process_info_t * procinfo; char * jack_client_name; int err; procinfo = g_malloc (sizeof (process_info_t)); procinfo->chain = NULL; procinfo->chain_end = NULL; #ifdef WITH_JACK procinfo->jack_client = NULL; procinfo->port_count = 0; procinfo->jack_input_ports = NULL; procinfo->jack_output_ports = NULL; #endif procinfo->channels = rack_channels; procinfo->quit = FALSE; if ( client_name == NULL ) { sample_rate = 48000; // should be set externally before calling process_ladspa buffer_size = MAX_BUFFER_SIZE; procinfo->silent_buffer = g_malloc (sizeof (LADSPA_Data) * buffer_size ); procinfo->jack_input_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels); procinfo->jack_output_buffers = g_malloc (sizeof (LADSPA_Data *) * rack_channels); return procinfo; } /* sort out the client name */ procinfo->jack_client_name = jack_client_name = strdup (client_name); for (err = 0; jack_client_name[err] != '\0'; err++) { if (jack_client_name[err] == ' ') jack_client_name[err] = '_'; else if (!isalnum (jack_client_name[err])) { /* shift all the chars up one (to remove the non-alphanumeric char) */ int i; for (i = err; jack_client_name[i] != '\0'; i++) jack_client_name[i] = jack_client_name[i + 1]; } else if (isupper (jack_client_name[err])) jack_client_name[err] = tolower (jack_client_name[err]); } #ifdef WITH_JACK err = process_info_connect_jack (procinfo); if (err) { /* g_free (procinfo); */ return NULL; /* abort (); */ } sample_rate = jack_get_sample_rate (procinfo->jack_client); buffer_size = jack_get_sample_rate (procinfo->jack_client); jack_set_process_callback (procinfo->jack_client, process_jack, procinfo); pthread_mutex_lock( &g_activate_mutex ); jack_on_shutdown (procinfo->jack_client, jack_shutdown_cb, procinfo); pthread_mutex_unlock( &g_activate_mutex ); jack_activate (procinfo->jack_client); err = process_info_set_port_count (procinfo, rack_channels, connect_inputs, connect_outputs); if (err) return NULL; #endif return procinfo; } void process_info_destroy (process_info_t * procinfo) { #ifdef WITH_JACK if (procinfo->jack_client) { jack_deactivate (procinfo->jack_client); jack_client_close (procinfo->jack_client); } g_free (procinfo->jack_input_ports); g_free (procinfo->jack_output_ports); #endif g_free (procinfo->jack_input_buffers); g_free (procinfo->jack_output_buffers); g_free (procinfo->silent_buffer); g_free (procinfo); } void process_quit (process_info_t * procinfo) { procinfo->quit = TRUE; }