~alextee/zrythm

63c804118e11938c96ae3d509673f7a7691c74d0 — Alexandros Theodotou 4 years ago 9e2ad16 carla_vst
add vst support through carla native (effects work but WIP)
M data/org.zrythm.Zrythm.gschema.xml => data/org.zrythm.Zrythm.gschema.xml +22 -1
@@ 204,7 204,7 @@
        be auto-connected.</description>
    </key>

    <!-- Selected language -->
    <!-- KEY: language -->
    <key name="language"
      enum="org.zrythm.Zrythm.preferences.language-enum">
      <default>"english"</default>


@@ 214,6 214,7 @@
      </description>
    </key>

    <!-- KEY: open-plugin-uis-on-instantiate -->
    <key name="open-plugin-uis-on-instantiate" type="i">
      <default>1</default>
      <summary>Open plugin UIs when they are instantiated</summary>


@@ 224,6 225,26 @@
      </description>
    </key>

    <!-- KEY: sfz-search-path -->
    <key name="sfz-search-paths" type="as">
      <default>[]</default>
      <summary>Search paths for SFZ instruments</summary>
      <description>
        The search paths to scan for SFZ
        instruments.
      </description>
    </key>

    <!-- KEY: sf2-search-path -->
    <key name="sf2-search-paths" type="as">
      <default>[]</default>
      <summary>Search paths for SF2 instruments</summary>
      <description>
        The search paths to scan for SF2
        instruments.
      </description>
    </key>

    <key name="edit-region-size" type="i">
      <default>4</default>
      <summary>Region Size</summary>

M inc/audio/automatable.h => inc/audio/automatable.h +6 -0
@@ 20,11 20,15 @@
#ifndef __AUDIO_AUTOMATABLE_H__
#define __AUDIO_AUTOMATABLE_H__

#include "config.h"

#include "audio/port.h"
#include "plugins/lv2/control.h"
#include "utils/yaml.h"

#ifdef HAVE_CARLA
#include "CarlaNativePlugin.h"
#endif

#define IS_AUTOMATABLE_LV2_CONTROL(x) x->type == AUTOMATABLE_TYPE_PLUGIN_CONTROL && \
                                   x->control


@@ 188,6 192,7 @@ automatable_create_lv2_control (
  Plugin *       plugin,
  Lv2Control * control);

#ifdef HAVE_CARLA
/**
 * Creates an automatable for a Carla native
 * plugin control.


@@ 196,6 201,7 @@ Automatable *
automatable_create_carla_control (
  Plugin *          plugin,
  const NativeParameter * param);
#endif

Automatable *
automatable_create_plugin_enabled (Plugin * plugin);

M inc/plugins/carla/engine_interface.h => inc/plugins/carla/engine_interface.h +1 -1
@@ 25,7 25,7 @@

#include "config.h"

#ifdef HAVE_CARLA_NATIVE_PLUGIN
#ifdef HAVE_CARLA

#ifndef __PLUGINS_CARLA_ENGINE_INTERFACE_H__
#define __PLUGINS_CARLA_ENGINE_INTERFACE_H__

M inc/plugins/carla/native_plugin.h => inc/plugins/carla/native_plugin.h +22 -12
@@ 25,12 25,13 @@

#include "config.h"

#ifdef HAVE_CARLA_NATIVE_PLUGIN
#ifdef HAVE_CARLA

#ifndef __PLUGINS_CARLA_NATIVE_PLUGIN_H__
#define __PLUGINS_CARLA_NATIVE_PLUGIN_H__

#include "CarlaNativePlugin.h"
#include <CarlaNativePlugin.h>
#include <CarlaUtils.h>

typedef struct PluginDescriptor PluginDescriptor;
typedef void * CarlaPluginHandle;


@@ 46,6 47,7 @@ typedef void * CarlaPluginHandle;
 */
typedef enum CarlaPluginType
{
  CARLA_PLUGIN_NONE,
  CARLA_PLUGIN_RACK,
  CARLA_PLUGIN_PATCHBAY,
  CARLA_PLUGIN_PATCHBAY16,


@@ 59,7 61,7 @@ typedef struct CarlaNativePlugin
  NativeHostDescriptor     host;
  const NativePluginDescriptor * descriptor;

  uint32_t                 midi_event_count;
  uint32_t                 num_midi_events;
  NativeMidiEvent          midi_events[200];
  NativeTimeInfo           time_info;



@@ 90,22 92,30 @@ typedef struct CarlaNativePlugin
 * Creates an instance of a CarlaNativePlugin inside
 * the given Plugin.
 *
 * The given Plugin must have its descriptor filled in.
 * The given Plugin must have its descriptor filled
 * in.
 */
void
carla_native_plugin_create (
  Plugin * plugin);

/**
 * Returns a filled in descriptor for the given
 * type.
 *
 * @param ins Set the descriptor to be an instrument.
 * Returns a filled in descriptor from the
 * given binary path.
 */
PluginDescriptor *
carla_native_plugin_get_descriptor_from_path (
  const char * path,
  PluginType   type);

/**
 * Returns a filled in descriptor from the
 * CarlaCachedPluginInfo.
 */
PluginDescriptor *
carla_native_plugin_get_descriptor (
  CarlaPluginType type,
  int             ins);
carla_native_plugin_get_descriptor_from_cached (
  const CarlaCachedPluginInfo * info,
  PluginType              type);

/**
 * Wrapper to get param count.


@@ 200,4 210,4 @@ carla_native_plugin_free (

#endif // header guard

#endif // HAVE_CARLA_NATIVE_PLUGIN
#endif // HAVE_CARLA

M inc/plugins/carla/plugin_interface.h => inc/plugins/carla/plugin_interface.h +61 -1
@@ 25,7 25,7 @@

#include "config.h"

#ifdef HAVE_CARLA_NATIVE_PLUGIN
#ifdef HAVE_CARLA

#ifndef __PLUGINS_CARLA_PLUGIN_INTERFACE_H__
#define __PLUGINS_CARLA_PLUGIN_INTERFACE_H__


@@ 56,6 56,66 @@ carla_plugin_process (
  float ** const cv_out,
  const uint32_t frames);

void
carla_plugin_get_real_name (
  CarlaPluginHandle handle,
  char * const      name);

void
carla_plugin_get_maker (
  CarlaPluginHandle handle,
  char * const      name);

int
carla_plugin_get_category (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_audio_in_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_audio_out_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_cv_in_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_cv_out_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_midi_in_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_midi_out_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_parameter_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_parameter_in_count (
  CarlaPluginHandle handle);

uint32_t
carla_plugin_get_parameter_out_count (
  CarlaPluginHandle handle);

int
carla_plugin_save_state_to_file (
  CarlaPluginHandle handle,
  const char * const filename);

int
carla_plugin_load_state_from_file (
  CarlaPluginHandle handle,
  const char * const filename);

#ifdef __cplusplus
}
#endif

M inc/plugins/plugin.h => inc/plugins/plugin.h +37 -6
@@ 92,13 92,28 @@ typedef enum PluginProtocol
{
  /** LV2 plugin. */
  PROT_LV2,
  /** DSSI. */
  PROT_DSSI,
  /** LADSPA. */
  PROT_LADSPA,
  /** VST2. */
  PROT_VST,
  /** VST3. **/
  PROT_VST3,

  /** Carla native plugin. */
  PROT_CARLA,
  /** AU (Mac). */
  PROT_AU,
  /** SoundFont 2 file. */
  PROT_SF2,
  /** SoundFont file. */
  PROT_SFZ,
  /** JACK application. */
  PROT_JACK,
#ifdef HAVE_CARLA
  /** Carla internal plugin (rack, patchbay, or
   * other plugins that are maintained by Carla
   * such as Bypass and LFO). */
  PROT_CARLA_INTERNAL,
#endif
} PluginProtocol;

typedef enum PluginArchitecture


@@ 140,12 155,18 @@ typedef struct PluginDescriptor
  PluginArchitecture   arch;
  /** Plugin protocol (Lv2/DSSI/LADSPA/VST...). */
  PluginProtocol       protocol;
  /** Path, if not an Lv2Plugin which uses URIs. */
  /** Path, if not an Lv2Plugin which uses URIs.
   * Carla will use this. */
  char                 * path;
  /** Lv2Plugin URI. */
  char                 * uri;
  /** Carla plugin type, if Carla. */
#ifdef HAVE_CARLA_NATIVE_PLUGIN
  /** 1 if this plugin is to be instantiated
   * through Carla. */
  int              open_with_carla;
  /** Carla plugin type, if Carla internal plugin. */
  CarlaPluginType  carla_type;
#endif
} PluginDescriptor;

/**


@@ 157,8 178,10 @@ typedef struct Plugin
  /** Pointer LV2 plugin, if LV2. */
  Lv2Plugin *          lv2;

#ifdef HAVE_CARLA
  /** Pointer to CarlaNativePlugin, if Carla. */
  CarlaNativePlugin *  carla;
#endif

  /** Descriptor. */
  PluginDescriptor *   descr;


@@ 262,7 285,8 @@ descriptor_fields_schema[] =
    PluginDescriptor, name,
   	0, CYAML_UNLIMITED),
  CYAML_FIELD_STRING_PTR (
    "website", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
    "website",
    CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
    PluginDescriptor, website,
   	0, CYAML_UNLIMITED),
  CYAML_FIELD_STRING_PTR (


@@ 476,6 500,13 @@ plugin_descriptor_string_to_category (
  const char * str);

/**
 * Frees the plugin descriptor.
 */
void
plugin_descriptor_free (
  PluginDescriptor * self);

/**
 * Moves the Plugin's automation from one Channel
 * to another.
 */

M inc/utils/io.h => inc/utils/io.h +16 -2
@@ 117,9 117,23 @@ io_rmdir (
 * @return a NULL terminated array of strings that
 *   must be free'd with g_strfreev().
 */
#define io_get_files_in_dir(dir) \
  io_get_files_in_dir_ending_in (dir, 0, NULL)

/**
 * Returns a list of the files in the given
 * directory.
 *
 * @param dir The directory to look for.
 *
 * @return a NULL terminated array of strings that
 *   must be free'd with g_strfreev().
 */
char **
io_get_files_in_dir (
  const char * dir);
io_get_files_in_dir_ending_in (
  const char * dir,
  const int    recursive,
  const char * end_string);

/**
 * Returns a newly allocated path that is either

M inc/utils/string.h => inc/utils/string.h +51 -0
@@ 33,6 33,16 @@
 */

/**
 * Returns if the character is ASCII.
 */
static inline int
string_char_is_ascii (
  const char character)
{
  return character >= 32 && character <= 126;
}

/**
 * Returns if the string is ASCII.
 */
int


@@ 40,6 50,15 @@ string_is_ascii (
  const char * string);

/**
 * Returns a new string up to the character before
 * the first non-ascii character, or until the
 * end.
 */
char *
string_stop_at_first_non_ascii (
  const char * str);

/**
 * Returns the matched string if the string array
 * contains the given substring.
 */


@@ 62,6 81,29 @@ string_contains_substr (
  const char * substr,
  const int    accept_alternatives);

int
string_ends_with (
  const char * str,
  const char * end_str,
  const int    ignore_case);

/**
 * Removes non-ascii characters from the given
 * string.
 */
void
string_remove_non_ascii_chars (
  char * str);

/**
 * Removes occurrences of the given character
 * from the string.
 */
void
string_remove_char (
  char *     str,
  const char remove);

/**
 * Returns if the two strings are equal.
 */


@@ 82,6 124,15 @@ string_convert_to_filename (
  const char * str);

/**
 * Returns the index of the first occurrence of
 * the search char, or -1 if not found.
 */
int
string_index_of_char (
  const char * str,
  const char   search_char);

/**
 * Removes any bak, bak1 etc suffixes from the
 * string and returns a newly allocated string.
 */

M meson.build => meson.build +40 -17
@@ 206,7 206,7 @@ test_strict_cflags = [
  '-Werror=overlength-strings',
  '-Werror=stringop-truncation',
  '-Werror=missing-declarations',
  '-Werror=redundant-decls',
  #'-Werror=redundant-decls',
  '-Werror=shadow',
  '-Werror=undef',
  '-Werror=unused',


@@ 332,23 332,11 @@ cyaml_dep = dependency(
if (cyaml_dep.found ())
  cdata.set('HAVE_CYAML', 1)
endif

carla_native_plugin_dep = dependency(
  'carla-native-plugin', required: false)
if (carla_native_plugin_dep.found ())
  cdata.set('HAVE_CARLA_NATIVE_PLUGIN', 1)
  carla_native_plugin_dep = declare_dependency(
    link_args: [
      '-Wl,-rpath=/usr/lib/carla',
      '-L/usr/lib/carla',
      '-lcarla_native-plugin',
      ],
    compile_args: [
      '-DREAL_BUILD',
      '-I/usr/include/carla',
      '-I/usr/include/carla/includes'
      ])
endif

carla_utils_dep = dependency(
  'carla-utils', required: false)
qt5_dep = dependency('Qt5Widgets', required: false)
yaml_dep = dependency('yaml-0.1')
gtk_dep = dependency('gtk+-3.0', version: '>=3.22')


@@ 364,7 352,6 @@ zrythm_deps = [
  alsa_dep,
  suil_dep,
  cyaml_dep,
  carla_native_plugin_dep,
  dependency('threads'),
  dependency('lilv-0', version: '>=0.24.2'),
  dependency('samplerate', version: '>=0.1.8'),


@@ 378,6 365,42 @@ if (qt5_dep.found() and get_option('enable_qt5'))
  zrythm_deps += qt5_dep
endif

if (carla_native_plugin_dep.found() and carla_utils_dep.found() and get_option('enable_carla'))
  cdata.set('HAVE_CARLA', 1)
  # workaround because carla's pc files are broken
  carla_includedir = carla_native_plugin_dep.get_pkgconfig_variable('includedir')
  carla_libdir = carla_native_plugin_dep.get_pkgconfig_variable('libdir')
  carla_native_plugin_dep = declare_dependency(
    link_args: [
      '-Wl,-rpath=' + carla_libdir,
      '-L' + carla_libdir,
      '-lcarla_native-plugin',
      ],
    compile_args: [
      '-DREAL_BUILD',
      '-I' + carla_includedir,
      '-I' + join_paths (carla_includedir, 'includes'),
      ])
  # workaround because carla's pc files are broken
  carla_includedir = carla_utils_dep.get_pkgconfig_variable('includedir')
  carla_libdir = carla_utils_dep.get_pkgconfig_variable('libdir')
  carla_utils_dep = declare_dependency(
    link_args: [
      '-Wl,-rpath=' + carla_libdir,
      '-L' + carla_libdir,
      '-lcarla_utils',
      ],
    compile_args: [
      '-DREAL_BUILD',
      '-I' + carla_includedir,
      '-I' + join_paths (carla_includedir, 'includes'),
      '-I' + join_paths (carla_includedir, 'utils'),
      ])
  zrythm_deps += [
    carla_native_plugin_dep,
    carla_utils_dep, ]
endif

if os_linux
  zrythm_deps += cc.find_library('rt')
endif

M meson_options.txt => meson_options.txt +6 -0
@@ 40,6 40,12 @@ option (
  description: 'Compile with ffmpeg (for MP3 support)')

option (
  'enable_carla',
  type: 'boolean',
  value: false,
  description: 'Compile with Carla (for VST support)')

option (
  'gen_dev_docs',
  type: 'boolean',
  value: false,

M src/audio/automatable.c => src/audio/automatable.c +2 -0
@@ 223,6 223,7 @@ automatable_create_lv2_control (
  return a;
}

#ifdef HAVE_CARLA
/**
 * Creates an automatable for a Carla native
 * plugin control.


@@ 252,6 253,7 @@ automatable_create_carla_control (

  return a;
}
#endif

Automatable *
automatable_create_plugin_enabled (

M src/plugins/carla/engine_interface.cpp => src/plugins/carla/engine_interface.cpp +7 -1
@@ 17,8 17,12 @@
 * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#ifdef HAVE_CARLA

#include "plugins/carla/engine_interface.h"
#include "CarlaEngine.hpp"
#include <CarlaEngine.hpp>

extern "C"
{


@@ 81,3 85,5 @@ carla_engine_get_plugin (
#undef GET_ENGINE

}

#endif // if HAVE_CARLA

M src/plugins/carla/native_plugin.c => src/plugins/carla/native_plugin.c +594 -140
@@ 17,9 17,14 @@
 * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#ifdef HAVE_CARLA

#include <stdlib.h>

#include "audio/engine.h"
#include "audio/midi.h"
#include "audio/transport.h"
#include "gui/widgets/main_window.h"
#include "plugins/plugin.h"


@@ 29,11 34,35 @@
#include "project.h"
#include "utils/gtk.h"
#include "utils/io.h"
#include "utils/string.h"
#include "zrythm.h"

#include <gtk/gtk.h>
#include <glib/gi18n.h>

/**
 * Tick callback for the plugin UI.
 */
static int
carla_plugin_tick_cb (
  GtkWidget * widget,
  GdkFrameClock * frame_clock,
  CarlaNativePlugin * self)
{
  if (self->plugin->visible &&
      MAIN_WINDOW)
    {
      CarlaPluginHandle plugin =
        carla_native_plugin_get_plugin_handle (
          self);
      carla_plugin_ui_idle (plugin);

      return G_SOURCE_CONTINUE;
    }
  else
    return G_SOURCE_REMOVE;
}

static uint32_t
host_get_buffer_size (
  NativeHostHandle handle)


@@ 121,8 150,61 @@ host_dispatcher (
  return 0;
}

/**
 * Carla engine callback.
 */
static void
engine_callback (
  void* ptr,
  EngineCallbackOpcode action,
  unsigned int pluginId,
  int value1,
  int value2,
  float value3,
  const char* valueStr)
{
  CarlaNativePlugin * self =
    (CarlaNativePlugin *) ptr;

  switch (action)
    {
    case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
      break;
    case ENGINE_CALLBACK_PLUGIN_ADDED:
      self->carla_plugin_id = pluginId;
      /*g_message ("Carla: plugin added");*/
      break;
    case ENGINE_CALLBACK_UI_STATE_CHANGED:
      if (value1 == 1)
        {
          self->plugin->visible = value1;
          g_message ("plugin ui visible");
        }
      else if (value1 == 0)
        {
          self->plugin->visible = value1;
        }
      else
        {
          g_warning (
            "Plugin \"%s\" UI crashed",
            self->plugin->descr->name);
        }
      break;
    case ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
    case ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
    case ENGINE_CALLBACK_IDLE:
    case ENGINE_CALLBACK_ENGINE_STOPPED:
      /* ignore */
      break;
    default:
      g_warn_if_reached ();
      break;
    }
}

static CarlaNativePlugin *
create (CarlaPluginType type)
_create ()
{
  CarlaNativePlugin * self =
    calloc (1, sizeof (CarlaNativePlugin));


@@ 171,6 253,15 @@ create (CarlaPluginType type)

  self->time_info.bbt.valid = 1;

  return self;
}

static CarlaNativePlugin *
create_carla_internal (CarlaPluginType type)
{
  CarlaNativePlugin * self =
    _create ();

  switch (type)
    {
    case CARLA_PLUGIN_RACK:


@@ 192,6 283,34 @@ create (CarlaPluginType type)
  return self;
}

static CarlaNativePlugin *
create_plugin (
  const PluginDescriptor * descr,
  PluginType   type)
{
  CarlaNativePlugin * self =
    _create ();

  /* instantiate the plugin to get its info */
  self->descriptor =
    carla_get_native_rack_plugin ();
  self->handle =
    self->descriptor->instantiate (
      &self->host);
  CarlaEngineHandle engine =
    carla_engine_get_from_native_plugin (
      self->descriptor, self->handle);
  carla_engine_set_callback (
    engine, engine_callback, self);
  self->carla_plugin_id = 0;
  carla_engine_add_plugin_simple (
    engine, type,
    descr->path, descr->name, descr->name,
    0, NULL);

  return self;
}

/**
 * Wrapper to get the CarlaPlugin instance.
 */


@@ 218,26 337,6 @@ carla_native_plugin_proces (
  const long          g_start_frames,
  const nframes_t     nframes)
{
  const float * inbuf[] =
    {
      self->stereo_in->l->buf,
      self->stereo_in->r->buf
    };
  float * outbuf[] =
    {
      self->stereo_out->l->buf,
      self->stereo_out->r->buf
    };
  const float * cvinbuf[] =
    {
      self->cv_in->l->buf,
      self->cv_in->r->buf
    };
  float * cvoutbuf[] =
    {
      self->cv_out->l->buf,
      self->cv_out->r->buf
    };

  self->time_info.playing =
    TRANSPORT_IS_ROLLING;


@@ 269,11 368,84 @@ carla_native_plugin_proces (
  self->time_info.bbt.beatsPerMinute =
    TRANSPORT->bpm;

  CarlaPluginHandle plugin =
    carla_native_plugin_get_plugin_handle (self);
  carla_plugin_process (
    plugin, inbuf,
    outbuf, cvinbuf, cvoutbuf, nframes);
  switch (self->plugin->descr->protocol)
    {
    case PROT_VST:
    {
      const float * inbuf[] =
        {
          self->stereo_in->l->buf,
          self->stereo_in->r->buf
        };
      float * outbuf[] =
        {
          self->stereo_out->l->buf,
          self->stereo_out->r->buf
        };
      const float * cvinbuf[] =
        {
          self->cv_in->l->buf,
          self->cv_in->r->buf
        };
      float * cvoutbuf[] =
        {
          self->cv_out->l->buf,
          self->cv_out->r->buf
        };
      CarlaPluginHandle plugin =
        carla_native_plugin_get_plugin_handle (self);
      g_warn_if_fail (plugin);
      carla_plugin_process (
        plugin, inbuf,
        outbuf, cvinbuf, cvoutbuf, nframes);
    }
      break;
    case PROT_SFZ:
    {
      float * inbuf[] =
        {
          self->stereo_in->l->buf,
          self->stereo_in->r->buf
        };
      float * outbuf[] =
        {
          self->stereo_out->l->buf,
          self->stereo_out->r->buf
        };
      for (int i = 0;
           i < self->midi_in->midi_events->
            num_events; i++)
        {
          MidiEvent * ev =
            &self->midi_in->midi_events->events[i];
          self->midi_events[i].time =
            ev->time;
          self->midi_events[i].size = 3;
          self->midi_events[i].data[0] =
            ev->raw_buffer[0];
          self->midi_events[i].data[1] =
            ev->raw_buffer[1];
          self->midi_events[i].data[2] =
            ev->raw_buffer[2];
        }
      self->num_midi_events =
        (uint32_t)
        self->midi_in->midi_events->num_events;
      if (self->num_midi_events > 0)
        {
          g_message (
            "Carla plugin %s has %d MIDI events",
            self->plugin->descr->name,
            self->num_midi_events);
        }
      self->descriptor->process (
        self->handle, inbuf, outbuf, nframes,
        self->midi_events, self->num_midi_events);
    }
      break;
    default:
      break;
    }
}

/**


@@ 281,43 453,378 @@ carla_native_plugin_proces (
 * type.
 *
 * @param ins Set the descriptor to be an instrument.
 *
 * FIXME delete
 */
/*PluginDescriptor **/
/*carla_native_plugin_get_descriptor (*/
  /*CarlaPluginType type,*/
  /*int             ins)*/
/*{*/
  /*CarlaNativePlugin * plugin =*/
    /*create (type);*/
  /*const NativePluginDescriptor * descr =*/
    /*plugin->descriptor;*/

  /*PluginDescriptor * self =*/
    /*calloc (1, sizeof (PluginDescriptor));*/

  /*self->author =*/
    /*g_strdup (descr->maker);*/
  /*self->name =*/
    /*g_strdup_printf (*/
      /*"Zrythm %s: %s",*/
      /*ins ? "Instrument" : "Effect",*/
      /*descr->name);*/
  /*if (ins)*/
    /*self->category = PC_INSTRUMENT;*/
  /*else*/
    /*self->category = PC_UTILITY;*/
  /*self->category_str =*/
    /*g_strdup (_("Plugin Adapter"));*/
  /*self->num_audio_ins = (int) descr->audioIns;*/
  /*self->num_audio_outs = (int) descr->audioOuts;*/
  /*self->num_midi_ins = (int) descr->midiIns;*/
  /*self->num_midi_outs = (int) descr->midiOuts;*/
  /*self->num_ctrl_ins = (int) descr->paramIns;*/
  /*self->num_ctrl_outs = (int) descr->paramOuts;*/
  /*self->protocol = PROT_CARLA;*/
  /*self->carla_type = type;*/

  /*carla_native_plugin_free (plugin);*/

  /*return self;*/
/*}*/

static ZPluginCategory
carla_category_to_zrythm_category (
  int category)
{
  switch (category)
    {
    case PLUGIN_CATEGORY_NONE:
      return PC_NONE;
      break;
    case PLUGIN_CATEGORY_SYNTH:
      return PC_INSTRUMENT;
      break;
    case PLUGIN_CATEGORY_DELAY:
      return PC_DELAY;
      break;
    case PLUGIN_CATEGORY_EQ:
      return PC_EQ;
      break;
    case PLUGIN_CATEGORY_FILTER:
      return PC_FILTER;
      break;
    case PLUGIN_CATEGORY_DISTORTION:
      return PC_DISTORTION;
      break;
    case PLUGIN_CATEGORY_DYNAMICS:
      return PC_DYNAMICS;
      break;
    case PLUGIN_CATEGORY_MODULATOR:
      return PC_MODULATOR;
      break;
    case PLUGIN_CATEGORY_UTILITY:
      return PC_UTILITY;
      break;
    case PLUGIN_CATEGORY_OTHER:
      return PC_NONE;
      break;
    }
  g_return_val_if_reached (PC_NONE);
}

static char *
carla_category_to_zrythm_category_str (
  int category)
{
  switch (category)
    {
    case PLUGIN_CATEGORY_NONE:
      return g_strdup ("Plugin");
      break;
    case PLUGIN_CATEGORY_SYNTH:
      return g_strdup ("Instrument");
      break;
    case PLUGIN_CATEGORY_DELAY:
      return g_strdup ("Delay");
      break;
    case PLUGIN_CATEGORY_EQ:
      return g_strdup ("Equalizer");
      break;
    case PLUGIN_CATEGORY_FILTER:
      return g_strdup ("Filter");
      break;
    case PLUGIN_CATEGORY_DISTORTION:
      return g_strdup ("Distortion");
      break;
    case PLUGIN_CATEGORY_DYNAMICS:
      return g_strdup ("Dynamics");
      break;
    case PLUGIN_CATEGORY_MODULATOR:
      return g_strdup ("Modulator");
      break;
    case PLUGIN_CATEGORY_UTILITY:
      return g_strdup ("Utility");
      break;
    case PLUGIN_CATEGORY_OTHER:
      return g_strdup ("Plugin");
      break;
    }
  g_return_val_if_reached (NULL);
}

/**
 * Returns a filled in descriptor from the
 * given binary path.
 */
PluginDescriptor *
carla_native_plugin_get_descriptor (
  CarlaPluginType type,
  int             ins)
carla_native_plugin_get_descriptor_from_path (
  const char * path,
  PluginType   type)
{
  CarlaNativePlugin * plugin =
    create (type);
  const NativePluginDescriptor * descr =
    plugin->descriptor;
  PluginDescriptor * descr =
    calloc (1, sizeof (PluginDescriptor));

  descr->path =
    g_strdup (path);

  descr->open_with_carla = 1;
  switch (type)
    {
    case PLUGIN_INTERNAL:
      descr->protocol = PROT_CARLA_INTERNAL;
      break;
    case PLUGIN_LADSPA:
      descr->protocol = PROT_LADSPA;
      break;
    case PLUGIN_DSSI:
      descr->protocol = PROT_DSSI;
      break;
    case PLUGIN_LV2:
      descr->protocol = PROT_LV2;
      break;
    case PLUGIN_VST2:
      descr->protocol = PROT_VST;
      break;
    case PLUGIN_SF2:
      descr->protocol = PROT_SF2;
      break;
    case PLUGIN_SFZ:
      descr->protocol = PROT_SFZ;
      break;
    case PLUGIN_JACK:
      descr->protocol = PROT_JACK;
      break;
    default:
      g_warn_if_reached ();
      break;
    }

  if (descr->protocol == PROT_VST)
    {
      char * carla_discovery_native =
        g_build_filename (
          carla_get_library_folder (),
          "carla-discovery-native",
          NULL);

      /* read plugin info */
      FILE *fp;
      char line[1035];

      /* Open the command for reading. */
      char * command =
        g_strdup_printf (
          "%s vst %s",
          carla_discovery_native,
          path);
      fp = popen (command, "r");
      g_free (command);
      if (fp == NULL)
        g_return_val_if_reached (NULL);

      /* Read the output a line at a time -
       * output it. */
      while (fgets (
                line, sizeof (line) - 1, fp) !=
               NULL)
        {
          if (g_str_has_prefix (
                line,
                "carla-discovery::name::"))
            {
              descr->name =
                g_strdup (
                  g_strchomp (&line[23]));
            }
          else if (
            g_str_has_prefix (
              line, "carla-discovery::maker::"))
            {
              descr->author =
                g_strdup (
                  g_strchomp (&line[24]));
            }
          else if (
            g_str_has_prefix (
              line,
              "carla-discovery::audio.ins::"))
            {
              descr->num_audio_ins =
                atoi (g_strchomp (&line[28]));
            }
          else if (
            g_str_has_prefix (
              line,
              "carla-discovery::audio.outs::"))
            {
              descr->num_audio_outs =
                atoi (g_strchomp (&line[29]));
            }
          else if (
            g_str_has_prefix (
              line,
              "carla-discovery::midi.ins::"))
            {
              descr->num_midi_ins =
                atoi (g_strchomp (&line[27]));
            }
          else if (
            g_str_has_prefix (
              line,
              "carla-discovery::midi.outs::"))
            {
              descr->num_midi_outs =
                atoi (g_strchomp (&line[28]));
            }
          else if (
            g_str_has_prefix (
              line,
              "carla-discovery::parameters.ins::"))
            {
              descr->num_ctrl_ins =
                atoi (g_strchomp (&line[33]));
            }
        }

      /* close */
      pclose (fp);

      descr->category = PC_NONE;
      descr->category_str =
        g_strdup ("Plugin");

      if (!descr->name)
        {
          plugin_descriptor_free (descr);
          return NULL;
        }
    }

  PluginDescriptor * self =
  return descr;
}

/**
 * Returns a filled in descriptor from the
 * CarlaCachedPluginInfo.
 */
PluginDescriptor *
carla_native_plugin_get_descriptor_from_cached (
  const CarlaCachedPluginInfo * info,
  PluginType              type)
{
  PluginDescriptor * descr =
    calloc (1, sizeof (PluginDescriptor));

  self->author =
    g_strdup (descr->maker);
  self->name =
    g_strdup_printf (
      "Zrythm %s: %s",
      ins ? "Instrument" : "Effect",
      descr->name);
  if (ins)
    self->category = PC_INSTRUMENT;
  else
    self->category = PC_UTILITY;
  self->category_str =
    g_strdup (_("Plugin Adapter"));
  self->num_audio_ins = (int) descr->audioIns;
  self->num_audio_outs = (int) descr->audioOuts;
  self->num_midi_ins = (int) descr->midiIns;
  self->num_midi_outs = (int) descr->midiOuts;
  self->num_ctrl_ins = (int) descr->paramIns;
  self->num_ctrl_outs = (int) descr->paramOuts;
  self->protocol = PROT_CARLA;
  self->carla_type = type;

  carla_native_plugin_free (plugin);
  descr->open_with_carla = 1;
  switch (type)
    {
    case PLUGIN_INTERNAL:
      descr->protocol = PROT_CARLA_INTERNAL;
      break;
    case PLUGIN_LADSPA:
      descr->protocol = PROT_LADSPA;
      break;
    case PLUGIN_DSSI:
      descr->protocol = PROT_DSSI;
      break;
    case PLUGIN_LV2:
      descr->protocol = PROT_LV2;
      break;
    case PLUGIN_VST2:
      descr->protocol = PROT_VST;
      break;
    case PLUGIN_SF2:
      descr->protocol = PROT_SF2;
      break;
    case PLUGIN_SFZ:
      descr->protocol = PROT_SFZ;
      break;
    case PLUGIN_JACK:
      descr->protocol = PROT_JACK;
      break;
    default:
      g_warn_if_reached ();
      break;
    }
  descr->name =
    g_strdup (info->name);
  descr->author =
    g_strdup (info->maker);
  descr->num_audio_ins = (int) info->audioIns;
  descr->num_audio_outs = (int) info->audioOuts;
  descr->num_midi_ins = (int) info->midiIns;
  descr->num_midi_outs = (int) info->midiOuts;
  descr->num_ctrl_ins = (int) info->parameterIns;
  descr->num_ctrl_outs = (int) info->parameterOuts;

  descr->category =
    carla_category_to_zrythm_category (
      info->category);
  descr->category_str =
    carla_category_to_zrythm_category_str (
      info->category);

  return descr;
}

static CarlaNativePlugin *
create_from_descr (
  PluginDescriptor * descr)
{
  g_return_val_if_fail (
    descr->open_with_carla ||
    (descr->protocol == PROT_CARLA_INTERNAL &&
       descr->carla_type > CARLA_PLUGIN_NONE),
    NULL);

  CarlaNativePlugin * self = NULL;
  switch (descr->protocol)
    {
    case PROT_LV2:
      self =
        create_plugin (descr, PLUGIN_LV2);
      break;
    case PROT_VST:
      self =
        create_plugin (descr, PLUGIN_VST2);
      break;
    case PROT_SFZ:
      self =
        create_plugin (descr, PLUGIN_SFZ);
      break;
    case PROT_CARLA_INTERNAL:
      create_carla_internal (
        descr->carla_type);
      break;
    default:
      g_warn_if_reached ();
      break;
    }
  g_warn_if_fail (self);

  return self;
}


@@ 333,7 840,7 @@ carla_native_plugin_create (
  Plugin * plugin)
{
  CarlaNativePlugin * self =
    create (plugin->descr->carla_type);
    create_from_descr (plugin->descr);

  plugin->carla = self;
  self->plugin = plugin;


@@ 454,52 961,6 @@ create_ports (
}

/**
 * Carla engine callback.
 */
static void
engine_callback (
  void* ptr,
  EngineCallbackOpcode action,
  unsigned int pluginId,
  int value1,
  int value2,
  float value3,
  const char* valueStr)
{
  CarlaNativePlugin * self =
    (CarlaNativePlugin *) ptr;

  switch (action)
    {
    case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
      break;
    case ENGINE_CALLBACK_PLUGIN_ADDED:
      self->carla_plugin_id = pluginId;
      break;
    case ENGINE_CALLBACK_UI_STATE_CHANGED:
      if (value1 == 1 ||
          value1 == 0)
        {
          self->plugin->visible = value1;
        }
      else
        {
          g_warning (
            "Plugin \"%s\" UI crashed",
            self->plugin->descr->name);
        }
      break;
    case ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
    case ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
      /* ignore */
      break;
    default:
      g_warn_if_reached ();
      break;
    }
}

/**
 * Instantiates the plugin.
 *
 * @ret 0 if no errors, non-zero if errors.


@@ 522,9 983,10 @@ carla_native_plugin_instantiate (
    engine, engine_callback, self);
  carla_engine_add_plugin_simple (
    engine, PLUGIN_VST2,
    "/usr/lib/vst/3BandEQ-vst.so",
    "3BandEQ VST",
    "label123", 0, NULL);
    self->plugin->descr->path,
    self->plugin->descr->name,
    self->plugin->descr->name,
    0, NULL);

  /* create ports */
  create_ports (self);


@@ 537,29 999,6 @@ carla_native_plugin_instantiate (
}

/**
 * Tick callback for the plugin UI.
 */
static int
carla_plugin_tick_cb (
  GtkWidget * widget,
  GdkFrameClock * frame_clock,
  CarlaNativePlugin * self)
{
  if (self->plugin->visible &&
      MAIN_WINDOW)
    {
      CarlaPluginHandle plugin =
        carla_native_plugin_get_plugin_handle (
          self);
      carla_plugin_ui_idle (plugin);

      return G_SOURCE_CONTINUE;
    }
  else
    return G_SOURCE_REMOVE;
}

/**
 * Shows or hides the UI.
 */
void


@@ 567,16 1006,29 @@ carla_native_plugin_show_ui (
  CarlaNativePlugin * self,
  int                 show)
{
  CarlaPluginHandle plugin =
    carla_native_plugin_get_plugin_handle (self);
  carla_plugin_show_custom_ui (
    plugin, show);

  g_warn_if_fail (MAIN_WINDOW);
  gtk_widget_add_tick_callback (
    GTK_WIDGET (MAIN_WINDOW),
    (GtkTickCallback) carla_plugin_tick_cb,
    self, NULL);
  switch (self->plugin->descr->protocol)
    {
    case PROT_VST:
      {
        CarlaPluginHandle plugin =
          carla_native_plugin_get_plugin_handle (
            self);
        g_warn_if_fail (plugin);
        carla_plugin_show_custom_ui (
          plugin, show);
        self->plugin->visible = 1;

        g_warn_if_fail (MAIN_WINDOW);
        gtk_widget_add_tick_callback (
          GTK_WIDGET (MAIN_WINDOW),
          (GtkTickCallback) carla_plugin_tick_cb,
          self, NULL);
      }
      break;
    case PROT_SFZ:
      break;
    default: break;
    }
}

/**


@@ 630,3 1082,5 @@ carla_native_plugin_free (

  free (self);
}

#endif // HAVE_CARLA

M src/plugins/carla/plugin_interface.cpp => src/plugins/carla/plugin_interface.cpp +129 -0
@@ 17,6 17,10 @@
 * along with Zrythm.  If not, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#ifdef HAVE_CARLA

#include "plugins/carla/plugin_interface.h"
#include "CarlaPlugin.hpp"



@@ 58,4 62,129 @@ carla_plugin_process (
    audio_in, audio_out, cv_in, cv_out, frames);
}

void
carla_plugin_get_real_name (
  CarlaPluginHandle handle,
  char * const      name)
{
  GET_PLUGIN;
  plugin->getRealName (name);
}

void
carla_plugin_get_maker (
  CarlaPluginHandle handle,
  char * const      name)
{
  GET_PLUGIN;
  plugin->getMaker (name);
}


int
carla_plugin_get_category (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getCategory ();
}

uint32_t
carla_plugin_get_audio_in_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getAudioInCount ();
}

uint32_t
carla_plugin_get_audio_out_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getAudioOutCount ();
}

uint32_t
carla_plugin_get_cv_in_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getCVInCount ();
}

uint32_t
carla_plugin_get_cv_out_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getCVOutCount ();
}

uint32_t
carla_plugin_get_midi_in_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getMidiInCount ();
}

uint32_t
carla_plugin_get_midi_out_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getMidiOutCount ();
}

uint32_t
carla_plugin_get_parameter_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  return plugin->getParameterCount ();
}

uint32_t
carla_plugin_get_parameter_in_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  uint32_t ins, outs;
  plugin->getParameterCountInfo (
    ins, outs);
  return ins;
}

uint32_t
carla_plugin_get_parameter_out_count (
  CarlaPluginHandle handle)
{
  GET_PLUGIN;
  uint32_t ins, outs;
  plugin->getParameterCountInfo (
    ins, outs);
  return outs;
}

int
carla_plugin_save_state_to_file (
  CarlaPluginHandle handle,
  const char * const filename)
{
  GET_PLUGIN;
  return plugin->saveStateToFile (filename);
}

int
carla_plugin_load_state_from_file (
  CarlaPluginHandle handle,
  const char * const filename)
{
  GET_PLUGIN;
  return plugin->loadStateFromFile (filename);
}

}

#endif // HAVE_CARLA

M src/plugins/plugin.c => src/plugins/plugin.c +142 -77
@@ 129,20 129,28 @@ plugin_new_from_descr (
    plugin_clone_descr (descr);
  plugin_init (plugin);

  switch (plugin->descr->protocol)
#ifdef HAVE_CARLA
  if (plugin->descr->open_with_carla)
    {
    case PROT_LV2:
      lv2_create_from_uri (plugin, descr->uri);
      break;
    case PROT_CARLA:
      carla_native_plugin_create (plugin);
      break;
    default:
      g_warning (
        "Plugin protocol %d not supported",
        plugin->descr->protocol);
      break;
    }
  else
    {
#endif
      switch (plugin->descr->protocol)
        {
        case PROT_LV2:
          lv2_create_from_uri (plugin, descr->uri);
          break;
        default:
          g_warning (
            "Plugin protocol %d not supported",
            plugin->descr->protocol);
          break;
        }
#ifdef HAVE_CARLA
    }
#endif

  return plugin;
}


@@ 191,7 199,10 @@ plugin_copy_descr (
  dest->protocol = src->protocol;
  dest->path = g_strdup (src->path);
  dest->uri = g_strdup (src->uri);
#ifdef HAVE_CARLA
  dest->open_with_carla = src->open_with_carla;
  dest->carla_type = src->carla_type;
#endif
}

/**


@@ 425,24 436,9 @@ plugin_generate_automation_tracks (
    plugin, at);

  /* add plugin control automatables */
  switch (plugin->descr->protocol)
#ifdef HAVE_CARLA
  if (plugin->descr->open_with_carla)
    {
    case PROT_LV2:
      for (int j = 0;
           j < plugin->lv2->controls.n_controls;
           j++)
        {
          Lv2Control * control =
            plugin->lv2->controls.controls[j];
          a =
            automatable_create_lv2_control (
              plugin, control);
          at = automation_track_new (a);
          plugin_add_automation_track (
            plugin, at);
        }
      break;
    case PROT_CARLA:
      for (uint32_t i = 0;
           i <
             carla_native_plugin_get_param_count (


@@ 459,13 455,36 @@ plugin_generate_automation_tracks (
          plugin_add_automation_track (
            plugin, at);
        }
      break;
    default:
      g_warning (
        "Plugin protocol not supported yet "
        "(gen automatables)");
      break;
    }
  else
    {
#endif
      switch (plugin->descr->protocol)
        {
        case PROT_LV2:
          for (int j = 0;
               j < plugin->lv2->controls.n_controls;
               j++)
            {
              Lv2Control * control =
                plugin->lv2->controls.controls[j];
              a =
                automatable_create_lv2_control (
                  plugin, control);
              at = automation_track_new (a);
              plugin_add_automation_track (
                plugin, at);
            }
          break;
        default:
          g_warning (
            "Plugin protocol not supported yet "
            "(gen automatables)");
          break;
        }
#ifdef HAVE_CARLA
    }
#endif
}

#define IS_CAT(x) \


@@ 639,6 658,27 @@ plugin_descriptor_string_to_category (
}

/**
 * Frees the plugin descriptor.
 */
void
plugin_descriptor_free (
  PluginDescriptor * self)
{
  if (self->category_str)
    g_free (self->category_str);
  if (self->author)
    g_free (self->author);
  if (self->website)
    g_free (self->website);
  if (self->path)
    g_free (self->path);
  if (self->uri)
    g_free (self->uri);

  free (self);
}

/**
 * Sets the track and track_pos on the plugin.
 */
void


@@ 677,27 717,34 @@ plugin_instantiate (
  g_message ("Instantiating %s...",
             pl->descr->name);

  switch (pl->descr->protocol)
#ifdef HAVE_CARLA
  if (pl->descr->open_with_carla)
    {
    case PROT_LV2:
      g_message ("state file: %s",
                 pl->lv2->state_file);
      if (lv2_plugin_instantiate (
            pl->lv2, NULL))
        {
          g_warning ("lv2 instantiate failed");
          return -1;
        }
      break;
    case PROT_CARLA:
      carla_native_plugin_instantiate (
        pl->carla);
      break;
    default:
      g_warn_if_reached ();
      return -1;
      break;
    }
  else
    {
#endif
      switch (pl->descr->protocol)
        {
        case PROT_LV2:
          g_message ("state file: %s",
                     pl->lv2->state_file);
          if (lv2_plugin_instantiate (
                pl->lv2, NULL))
            {
              g_warning ("lv2 instantiate failed");
              return -1;
            }
          break;
        default:
          g_return_val_if_reached (-1);
          break;
        }
#ifdef HAVE_CARLA
    }
#endif
  pl->enabled = 1;

  return 0;


@@ 715,19 762,27 @@ plugin_process (
  const long      g_start_frames,
  const nframes_t nframes)
{
  switch (plugin->descr->protocol)
#ifdef HAVE_CARLA
  if (plugin->descr->open_with_carla)
    {
    case PROT_LV2:
      lv2_plugin_process (
        plugin->lv2, g_start_frames, nframes);
      break;
    case PROT_CARLA:
      carla_native_plugin_proces (
        plugin->carla, g_start_frames, nframes);
      break;
    default:
      g_warn_if_reached ();
    }
  else
    {
#endif
      switch (plugin->descr->protocol)
        {
        case PROT_LV2:
          lv2_plugin_process (
            plugin->lv2, g_start_frames, nframes);
          break;
        default:
          g_warn_if_reached ();
        }
#ifdef HAVE_CARLA
    }
#endif
}

/**


@@ 736,26 791,34 @@ plugin_process (
void
plugin_open_ui (Plugin *plugin)
{
  switch (plugin->descr->protocol)
#ifdef HAVE_CARLA
  if (plugin->descr->open_with_carla)
    {
    case PROT_LV2:
      if (GTK_IS_WINDOW (plugin->lv2->window))
        {
          gtk_window_present (
            GTK_WINDOW (plugin->lv2->window));
        }
      else
        {
          lv2_open_ui (plugin->lv2);
        }
      break;
    case PROT_CARLA:
      carla_native_plugin_show_ui (
        plugin->carla, 1);
      break;
    default:
      g_return_if_reached ();
    }
  else
    {
#endif
      switch (plugin->descr->protocol)
        {
        case PROT_LV2:
          if (GTK_IS_WINDOW (plugin->lv2->window))
            {
              gtk_window_present (
                GTK_WINDOW (plugin->lv2->window));
            }
          else
            {
              lv2_open_ui (plugin->lv2);
            }
          break;
        default:
          g_return_if_reached ();
        }
#ifdef HAVE_CARLA
    }
#endif
}

/**


@@ 828,10 891,12 @@ plugin_clone (
      /* delete the state file */
      io_remove (pl->lv2->state_file);
    }
  else if (prot == PROT_CARLA)
#ifdef HAVE_CARLA
  else if (prot == PROT_CARLA_INTERNAL)
    {
      /* TODO */
    }
#endif

  g_return_val_if_fail (clone, NULL);
  clone->slot = pl->slot;

M src/plugins/plugin_manager.c => src/plugins/plugin_manager.c +166 -14
@@ 29,6 29,7 @@
#include "plugins/plugin_manager.h"
#include "plugins/lv2_plugin.h"
#include "utils/arrays.h"
#include "utils/io.h"
#include "utils/string.h"
#include "utils/ui.h"
#include "zrythm.h"


@@ 36,6 37,10 @@
#include <gtk/gtk.h>
#include <glib/gi18n.h>

#ifdef HAVE_CARLA
#include <CarlaUtils.h>
#endif

#include <lv2/lv2plug.in/ns/ext/event/event.h>
#include <lv2/lv2plug.in/ns/ext/options/options.h>
#include <lv2/lv2plug.in/ns/ext/parameters/parameters.h>


@@ 127,13 132,11 @@ static int sort_plugin_func (
    /*}*/
/*}*/

/**
 * scans for plugins.
 */
static void
scan_plugins (PluginManager * self)
scan_and_add_lv2 (
  PluginManager * self)
{
  g_message ("scanning plugins...");
  g_message ("Scanning LV2...");

  /* load all plugins with lilv */
  LilvWorld * world = LILV_WORLD;


@@ 161,24 164,173 @@ scan_plugins (PluginManager * self)
            self, descriptor->category_str);
        }
    }
}

  /* add carla */
  for (int i = CARLA_PLUGIN_RACK;
       i <= CARLA_PLUGIN_PATCHBAY; i++)
#ifdef HAVE_CARLA
static void
scan_and_add_vst2 (
  PluginManager * self)
{
  char * vst_path =
    g_strdup (getenv ("VST_PATH"));
  if (!vst_path)
    vst_path =
      g_strdup ("/usr/lib/vst:/usr/local/lib/vst");

  char ** paths =
    g_strsplit (vst_path, ":", 0);
  int path_idx = 0;
  char * path;
  while ((path = paths[path_idx++]) != NULL)
    {
      for (int j = 0; j < 2; j++)
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
        continue;

      char ** plugins =
        io_get_files_in_dir_ending_in (
          path, 1, ".so");
      if (!plugins)
        continue;

      char * plugin;
      int plugin_idx = 0;
      while ((plugin = plugins[plugin_idx++]) !=
               NULL)
        {
          PluginDescriptor * descr =
            carla_native_plugin_get_descriptor (
              i, j);
          g_warn_if_fail (descr);
          PluginDescriptor * descriptor =
            carla_native_plugin_get_descriptor_from_path (
              plugin, PLUGIN_VST2);

          if (descriptor)
            {
              /*g_message ("found VST2: %s",*/
                         /*descriptor->name);*/
              array_append (
                self->plugin_descriptors,
                self->num_plugins,
                descriptor);
              add_category (
                self, descriptor->category_str);
            }
        }
      g_strfreev (plugins);
    }

  g_free (vst_path);
}
#endif

#ifdef HAVE_CARLA
static void
scan_and_add_sfz (
  PluginManager * self)
{
  /* scan SFZ */
  char ** sfz_search_paths =
    g_settings_get_strv (
      S_PREFERENCES, "sfz-search-paths");
  char * path;
  int path_idx = 0;
  while ((path = sfz_search_paths[path_idx++]) !=
           NULL)
    {
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
        continue;

      unsigned int count =
        carla_get_cached_plugin_count (
          PLUGIN_SFZ, path);
      for (unsigned int i = 0; i < count; i++)
        {
          const CarlaCachedPluginInfo * info =
            carla_get_cached_plugin_info (
              PLUGIN_SFZ, i);

          if (!info->valid)
            continue;

          PluginDescriptor * descriptor =
            carla_native_plugin_get_descriptor_from_cached (
              info, PLUGIN_SFZ);
          /*g_message ("found SFZ: %s", info->name);*/

          array_append (
            self->plugin_descriptors,
            self->num_plugins,
            descr);
            descriptor);
          add_category (
            self, descriptor->category_str);
        }
    }
  g_strfreev (sfz_search_paths);
}
#endif

#ifdef HAVE_CARLA
static void
scan_and_add_sf2 (
  PluginManager * self)
{
  /* scan SF2 */
  char ** sf2_search_paths =
    g_settings_get_strv (
      S_PREFERENCES, "sf2-search-paths");
  char * path;
  int path_idx = 0;
  while ((path = sf2_search_paths[path_idx++]) !=
           NULL)
    {
      if (!g_file_test (path, G_FILE_TEST_EXISTS))
        continue;

      char ** plugins =
        io_get_files_in_dir_ending_in (
          path, 1, ".sf2");
      if (!plugins)
        continue;

      char * plugin;
      int plugin_idx = 0;
      while ((plugin = plugins[plugin_idx++]) !=
               NULL)
        {
          PluginDescriptor * descriptor =
            carla_native_plugin_get_descriptor_from_path (
              plugin, PLUGIN_SF2);

          if (descriptor)
            {
              g_message ("found SF2: %s",
                         descriptor->name);
              array_append (
                self->plugin_descriptors,
                self->num_plugins,
                descriptor);
              add_category (
                self, descriptor->category_str);
            }
        }
      g_strfreev (plugins);
    }
  g_strfreev (sf2_search_paths);
}
#endif

/**
 * scans for plugins.
 */
static void
scan_plugins (PluginManager * self)
{
  g_message ("scanning plugins...");

  scan_and_add_lv2 (self);
#ifdef HAVE_CARLA
  scan_and_add_sfz (self);
  (void) scan_and_add_sf2;
  /*scan_and_add_sf2 (self);*/
  scan_and_add_vst2 (self);
#endif

  /* sort alphabetically */
  qsort (PLUGIN_MANAGER->plugin_descriptors,

M src/utils/io.c => src/utils/io.c +63 -18
@@ 215,39 215,84 @@ io_rmdir (
}

/**
 * Returns a list of the files in the given
 * directory.
 * Appends files to the given array from the given
 * dir if they end in the given string.
 *
 * @return a NULL terminated array of strings that
 *   must be free'd with g_strfreev().
 * @param end_string If empty, appends all files.
 */
char **
io_get_files_in_dir (
  const char * _dir)
static void
append_files_from_dir_ending_in (
  char ***     files,
  int *        num_files,
  const int    recursive,
  const char * _dir,
  const char * end_string)
{
  GDir *dir;
  GError *error;
  const gchar *filename;

  char ** arr =
    calloc (1, sizeof (char *));
  int count = 0;
  char * full_path;

  dir = g_dir_open (_dir, 0, &error);
  while ((filename = g_dir_read_name (dir)))
    {
      arr =
        realloc (
          arr,
          sizeof (char *) * (size_t) (count + 2));
      arr[count] =
      full_path =
        g_build_filename (
          _dir, filename, NULL);
      count++;

      /* recurse if necessary */
      if (recursive &&
          g_file_test (
            full_path, G_FILE_TEST_IS_DIR))
        {
          append_files_from_dir_ending_in (
            files, num_files, recursive, full_path,
            end_string);
        }

      if (!end_string ||
          (end_string &&
             g_str_has_suffix (
               full_path, end_string)))
        {
          *files =
            realloc (
              *files,
              sizeof (char *) *
                (size_t) (*num_files + 2));
          (*files)[(*num_files)] =
            g_strdup (full_path);
          (*num_files)++;
        }

      g_free (full_path);
    }

  /* NULL terminate */
  arr[count] = NULL;
  (*files)[*num_files] = NULL;
}

/**
 * Returns a list of the files in the given
 * directory.
 *
 * @param dir The directory to look for.
 *
 * @return a NULL terminated array of strings that
 *   must be free'd with g_strfreev().
 */
char **
io_get_files_in_dir_ending_in (
  const char * _dir,
  const int    recursive,
  const char * end_string)
{
  char ** arr =
    calloc (1, sizeof (char *));
  int count = 0;

  append_files_from_dir_ending_in (
    &arr, &count, recursive, _dir, end_string);

  return arr;
}

M src/utils/string.c => src/utils/string.c +107 -2
@@ 21,10 21,14 @@

#include <string.h>

#include "utils/arrays.h"
#include "utils/string.h"

#include <gtk/gtk.h>

/**
 * Returns if the string is ASCII.
 */
int
string_is_ascii (const char * string)
{


@@ 33,8 37,7 @@ string_is_ascii (const char * string)
    return 0;
  for (i = 0; i < strlen (string); i++)
    {
      if (string[i] < 32 ||
          string[i] > 126)
      if (!string_char_is_ascii (string[i]))
        {
          return 0;
        }


@@ 43,6 46,25 @@ string_is_ascii (const char * string)
}

/**
 * Returns a new string up to the character before
 * the first non-ascii character, or until the
 * end.
 */
char *
string_stop_at_first_non_ascii (
  const char * str)
{
  for (int i = 0; i < (int) strlen (str); i++)
    {
      if (!string_char_is_ascii (str[i]))
        return
          g_strdup_printf (
            "%.*s", i, str);
    }
  return g_strdup (str);
}

/**
 * Returns the matched string if the string array
 * contains the given substring.
 */


@@ 115,6 137,16 @@ string_contains_substr (
      accept_alternatives);
}

int
string_ends_with (
  const char * str,
  const char * end_str,
  const int    ignore_case)
{
  /* TODO */
  g_return_val_if_reached (-1);
}

/**
 * Returns a newly allocated string that is a
 * filename version of the given string.


@@ 153,6 185,79 @@ string_convert_to_filename (
}

/**
 * Removes non-ascii characters from the given
 * string.
 */
void
string_remove_non_ascii_chars (
  char * str)
{
  int count, len;
  for (len = 0; str[len] != '\0'; ++len);
  g_warn_if_fail (len > 0);

  count = len - 1;
  while (count >= 0 && str[count] != 0)
    {
      char chr = str[count];
      if (chr < 32 ||
          chr > 126)
        {
          array_delete (
            str, len, chr);
          len--;
        }
      count--;
    }
}

/**
 * Removes occurrences of the given character
 * from the string.
 */
void
string_remove_char (
  char *     str,
  const char remove)
{
  int count, len;
  for (len = 0; str[len] != '\0'; ++len);
  g_warn_if_fail (len > 0);

  count = len - 1;
  while (count >= 0 && str[count] != 0)
    {
      char chr = str[count];
      if (chr == remove)
        {
          array_delete (
            str, len, chr);
          len--;
        }
      count--;
    }
}

/**
 * Returns the index of the first occurrence of
 * the search char, or -1 if not found.
 */
int
string_index_of_char (
  const char * str,
  const char   search_char)
{
  int len = (int) strlen (str);
  for (int i = 0; i < len; i++)
    {
      if (str[i] == search_char)
        return i;
    }

  return -1;
}

/**
 * Removes any bak, bak1 etc suffixes from the
 * string and returns a newly allocated string.
 */

M src/zrythm.c => src/zrythm.c +3 -1
@@ 171,8 171,10 @@ init_recent_projects ()
        }

      ZRYTHM->recent_projects[
        ZRYTHM->num_recent_projects++] = prj;
        ZRYTHM->num_recent_projects++] =
          g_strdup (prj);
    }
  g_strfreev (recent_projects);

  /* set last element to NULL because the call
   * takes a NULL terminated array */