[beast/wip/soundfont: 829/832] Merge branch 'master' of github.com/tim-janik/beast into soundfont



commit b2a845e3d86e7a8a1f27ed83718608c33e76534f
Merge: 9b2fd45 12973ff
Author: Stefan Westerfeld <timj gnu org>
Date:   Tue Oct 18 14:46:42 2016 +0200

    Merge branch 'master' of github.com/tim-janik/beast into soundfont
    
    Replace old .proc files with proper bseapi.idl entries.
    
    Signed-off-by: Stefan Westerfeld <stefan space twc de>

 beast-gtk/Makefile.am         |    2 +-
 beast-gtk/bstapp.cc           |  168 ++++++++++----------
 beast-gtk/bstapp.hh           |    1 +
 beast-gtk/bstbseutils.cc      |   65 ++++----
 beast-gtk/bstbseutils.hh      |    2 +-
 beast-gtk/bstbusmixer.cc      |   14 +-
 beast-gtk/bstcanvassource.cc  |    6 +-
 beast-gtk/bstfiledialog.cc    |   28 ++--
 beast-gtk/bstmain.cc          |   13 +-
 beast-gtk/bstmenus.cc         |   47 +++----
 beast-gtk/bstmenus.hh         |   24 ++--
 beast-gtk/bstpartdialog.cc    |    7 +-
 beast-gtk/bstpianorollctrl.cc |   14 ++-
 beast-gtk/bstplayback.cc      |    3 +-
 beast-gtk/bstprojectctrl.cc   |   25 ++--
 beast-gtk/bstprojectctrl.hh   |    1 +
 beast-gtk/bstsnetrouter.cc    |   22 +--
 beast-gtk/bstsoundfontview.cc |    6 +-
 beast-gtk/bsttrackview.cc     |   24 ++--
 beast-gtk/bsttreestores.cc    |   13 +-
 beast-gtk/bstusermessage.cc   |    6 +-
 beast-gtk/bstutils.cc         |   62 +++-----
 beast-gtk/bstutils.hh         |   16 +--
 beast-gtk/bstwaveeditor.cc    |   21 ++--
 beast-gtk/bstwaveview.cc      |   10 +-
 beast-gtk/gxk/Makefile.am     |    2 +-
 bse/Makefile.am               |   11 +-
 bse/bseapi.idl                |  127 +++++++++++-----
 bse/bsebasics.idl             |   19 ---
 bse/bsecategories.cc          |  270 ++++++++++-----------------------
 bse/bsecategories.hh          |   30 +---
 bse/bsecategories.proc        |   72 ---------
 bse/bsecontainer.cc           |   56 ++++----
 bse/bsecontainer.hh           |    3 +-
 bse/bsecontainer.proc         |   60 -------
 bse/bsecsynth.cc              |    1 +
 bse/bseengine.cc              |    2 +-
 bse/bseenginemaster.cc        |    2 +-
 bse/bseenginenode.hh          |    2 +-
 bse/bseglue.cc                |   21 +--
 bse/bseitem.cc                |   10 +-
 bse/bsemain.cc                |    1 -
 bse/bsemidisynth.cc           |    2 +
 bse/bseobject.cc              |   57 +++----
 bse/bseobject.hh              |    4 +-
 bse/bseprocidl.cc             |   12 +-
 bse/bseproject.cc             |  202 +++++++++++++++++++++----
 bse/bseproject.hh             |   67 +++++----
 bse/bseproject.proc           |  340 -----------------------------------------
 bse/bseserver.cc              |   12 ++
 bse/bseserver.hh              |    2 +
 bse/bsesong.cc                |  151 ++++++++++++++++---
 bse/bsesong.hh                |    5 +
 bse/bsesong.proc              |  225 ---------------------------
 bse/bsesoundfont.cc           |   11 ++
 bse/bsesoundfont.hh           |   12 ++
 bse/bsesoundfontrepo.cc       |  101 ++++++++++++
 bse/bsesoundfontrepo.hh       |   14 ++
 bse/bsesoundfontrepo.proc     |  155 -------------------
 bse/bsesuper.cc               |    9 +-
 bse/bsetool.cc                |    7 +-
 bse/bseutils.cc               |   69 ++++-----
 bse/bseutils.hh               |    2 +-
 bse/bsewave.cc                |   41 +++++
 bse/bsewave.hh                |    6 +-
 bse/bsewave.proc              |  133 ----------------
 bse/bsewaverepo.cc            |   65 ++++++++-
 bse/bsewaverepo.hh            |    4 +-
 bse/bsewaverepo.proc          |  117 --------------
 configure.ac                  |   13 ++
 plugins/Makefile.am           |    2 +-
 plugins/bseadder.cc           |    7 +-
 plugins/bseadder.hh           |    2 +-
 po/Makefile.am                |    2 +-
 po/POTSCAN                    |    9 +-
 sfi/sfitypes.hh               |    2 +-
 sfi/sfiwrapper.hh             |    2 +-
 77 files changed, 1221 insertions(+), 1932 deletions(-)
---
diff --cc beast-gtk/bstfiledialog.cc
index 05cd582,9c7d01e..3ce886c
--- a/beast-gtk/bstfiledialog.cc
+++ b/beast-gtk/bstfiledialog.cc
@@@ -752,37 -738,6 +751,36 @@@ bst_file_dialog_load_wave (BstFileDialo
  }
  
  GtkWidget*
 +bst_file_dialog_popup_load_sound_font (gpointer parent_widget,
 +                                     SfiProxy sound_font_repo,
 +                                     gboolean show_lib)
 +{
 +  BstFileDialog *self = bst_file_dialog_global_sound_font ();
 +  GtkWidget *widget = GTK_WIDGET (self);
 +
 +  bst_file_dialog_set_mode (self, parent_widget,
 +                          show_lib ? BST_FILE_DIALOG_LOAD_SOUND_FONT_LIB : BST_FILE_DIALOG_LOAD_SOUND_FONT,
 +                          _("Load Sound Font"), Bse::ProjectH(), 0, sound_font_repo);
 +  gxk_widget_showraise (widget);
 +
 +  return widget;
 +}
 +
 +static gboolean
 +bst_file_dialog_load_sound_font (BstFileDialog *self,
 +                               const gchar   *file_name)
 +{
-   Bse::Error error;
- 
 +  gxk_status_printf (0, NULL, _("Loading sound font `%s'"), file_name);
-   error = bse_sound_font_repo_load_file (self->sound_font_repo, file_name);
++  Bse::SoundFontRepoH repo = Bse::SoundFontRepoH::down_cast (bse_server.from_proxy (self->sound_font_repo));
++  Bse::Error error = repo.load_file (file_name);
 +  bst_status_eprintf (error, _("Loading sound font `%s'"), file_name);
 +  if (error != 0)
 +    sfi_error (_("Failed to load sound font \"%s\": %s"), file_name, Bse::error_blurb (error));
 +
 +  return TRUE;
 +}
 +
 +GtkWidget*
  bst_file_dialog_create (void)
  {
    BstFileDialog *self = (BstFileDialog*) g_object_new (BST_TYPE_FILE_DIALOG, NULL);
diff --cc beast-gtk/bstsoundfontview.cc
index d93de99,0000000..0e40d0b
mode 100644,000000..100644
--- a/beast-gtk/bstsoundfontview.cc
+++ b/beast-gtk/bstsoundfontview.cc
@@@ -1,139 -1,0 +1,143 @@@
 +// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 +#include "bstsoundfontview.hh"
 +#include "bstsoundfontpresetview.hh"
 +#include "bstfiledialog.hh"
 +
 +/* --- prototypes --- */
 +
 +static void     sound_font_view_action_exec           (gpointer                data,
 +                                                       gulong                  action);
 +static gboolean sound_font_view_action_check          (gpointer                data,
 +                                                       gulong                  action,
 +                                                       guint64                 action_stamp);
 +
 +
 +/* --- sound font actions --- */
 +
 +enum {
 +  ACTION_LOAD_SOUND_FONT,
 +  ACTION_LOAD_SOUND_FONT_LIB,
 +  ACTION_DELETE_SOUND_FONT,
 +  ACTION_SOUND_FONT_LAST
 +};
 +static const GxkStockAction sound_font_view_actions[] = {
 +  { N_("Load..."),  NULL,       N_("Load a new sound font file from disk"),
 +    ACTION_LOAD_SOUND_FONT,     BST_STOCK_LOAD,       },
 +  { N_("Lib..."),   NULL,       N_("Load a sound font file from library paths"),
 +    ACTION_LOAD_SOUND_FONT_LIB,       BST_STOCK_LOAD_LIB, },
 +  { N_("Delete"),   NULL,       N_("Delete the currently selected sound font from project"),
 +    ACTION_DELETE_SOUND_FONT,   BST_STOCK_TRASHCAN, },
 +};
 +
 +
 +/* --- functions --- */
 +
 +G_DEFINE_TYPE (BstSoundFontView, bst_sound_font_view, BST_TYPE_ITEM_VIEW);
 +
 +static void
 +bst_sound_font_view_class_init (BstSoundFontViewClass *klass)
 +{
 +  BstItemViewClass *item_view_class = BST_ITEM_VIEW_CLASS (klass);
 +
 +  item_view_class->item_type = "BseSoundFont";
 +}
 +
 +static void
 +sound_font_selection_changed (BstSoundFontView *self)
 +{
 +  BstItemView *iview = BST_ITEM_VIEW (self);
 +  bst_item_view_set_container (BST_ITEM_VIEW (self->preset_view), bst_item_view_get_current (iview));
 +}
 +
 +static void
 +bst_sound_font_view_init (BstSoundFontView *self)
 +{
 +  BstItemView *iview = BST_ITEM_VIEW (self);
 +  /* complete GUI */
 +  GxkRadget *radget = gxk_radget_complete (GTK_WIDGET (self), "beast", "sound-font-view", NULL);
 +  gxk_widget_publish_actions (self, "sound-font-view-actions",
 +                              G_N_ELEMENTS (sound_font_view_actions), sound_font_view_actions,
 +                              NULL, sound_font_view_action_check, sound_font_view_action_exec);
 +  /* setup tree view */
 +  GtkTreeView *tview = (GtkTreeView *) gxk_radget_find (radget, "tree-view");
 +  bst_item_view_complete_tree (iview, tview);
 +
 +  g_object_connect (gtk_tree_view_get_selection (tview),
 +                    "swapped_object_signal::changed", sound_font_selection_changed, self,
 +                    NULL);
 +
 +
 +  /* setup preset view */
 +  GtkTreeView *pview = (GtkTreeView *) gxk_radget_find (radget, "preset-view");
 +  self->preset_view = BST_SOUND_FONT_PRESET_VIEW (bst_sound_font_preset_view_new());
 +  bst_item_view_complete_tree (BST_ITEM_VIEW (self->preset_view), pview);
 +}
 +
 +GtkWidget*
 +bst_sound_font_view_new (SfiProxy sfrepo)
 +{
 +  GtkWidget *sound_font_view;
 +
 +  g_return_val_if_fail (BSE_IS_SOUND_FONT_REPO (sfrepo), NULL);
 +
 +  sound_font_view = gtk_widget_new (BST_TYPE_SOUND_FONT_VIEW, NULL);
 +  bst_item_view_set_container (BST_ITEM_VIEW (sound_font_view), sfrepo);
 +
 +  return sound_font_view;
 +}
 +
 +SfiProxy
 +bst_sound_font_view_get_preset (BstSoundFontView *self)
 +{
 +  return bst_item_view_get_current (BST_ITEM_VIEW (self->preset_view));
 +}
 +
 +static void
 +sound_font_view_action_exec (gpointer                data,
 +                             gulong                  action)
 +{
 +  BstSoundFontView *self = BST_SOUND_FONT_VIEW (data);
 +  BstItemView *item_view = BST_ITEM_VIEW (self);
 +  SfiProxy sfrepo = item_view->container;
 +  switch (action)
 +    {
 +      SfiProxy item;
 +    case ACTION_LOAD_SOUND_FONT:
 +      bst_file_dialog_popup_load_sound_font (item_view, BST_ITEM_VIEW (self)->container, FALSE);
 +      break;
 +    case ACTION_LOAD_SOUND_FONT_LIB:
 +      bst_file_dialog_popup_load_sound_font (item_view, BST_ITEM_VIEW (self)->container, TRUE);
 +      break;
 +    case ACTION_DELETE_SOUND_FONT:
 +      item = bst_item_view_get_current (BST_ITEM_VIEW (self));
-       bse_sound_font_repo_remove_sound_font (sfrepo, item);
++      {
++        Bse::SoundFontRepoH repo = Bse::SoundFontRepoH::down_cast (bse_server.from_proxy (sfrepo));
++        Bse::SoundFontH sound_font = Bse::SoundFontH::down_cast (bse_server.from_proxy (item));
++        repo.remove_sound_font (sound_font);
++      }
 +      break;
 +    default:
 +      break;
 +    }
 +  gxk_widget_update_actions_downwards (self);
 +}
 +
 +static gboolean
 +sound_font_view_action_check (gpointer                data,
 +                              gulong                  action,
 +                              guint64                 action_stamp)
 +{
 +  BstSoundFontView *self = BST_SOUND_FONT_VIEW (data);
 +  BstItemView *item_view = BST_ITEM_VIEW (self);
 +
 +  switch (action)
 +    {
 +    case ACTION_LOAD_SOUND_FONT:
 +    case ACTION_LOAD_SOUND_FONT_LIB:
 +      return TRUE;
 +    case ACTION_DELETE_SOUND_FONT:
 +      return bst_item_view_get_current (item_view) != 0;
 +    default:
 +      return FALSE;
 +    }
 +}
diff --cc beast-gtk/bsttrackview.cc
index 1cc8ea9,63f63ae..8ead85e
--- a/beast-gtk/bsttrackview.cc
+++ b/beast-gtk/bsttrackview.cc
@@@ -139,21 -140,11 +140,21 @@@ track_view_fill_value (BstItemView *ivi
        break;
      case COL_SYNTH:
        snet = 0;
 -      bse_proxy_get (item.proxy_id(), "snet", &snet, "wave", &wave, NULL);
 -      g_value_set_string (value, snet || wave ? bse_item_get_name (snet ? snet : wave) : "");
 +      wave = 0;
 +      sound_font_preset = 0;
-       bse_proxy_get (item, "snet", &snet, "wave", &wave, "sound_font_preset", &sound_font_preset, NULL);
++      bse_proxy_get (item.proxy_id(), "snet", &snet, "wave", &wave, "sound_font_preset", 
&sound_font_preset, NULL);
 +      if (snet)
 +      string = bse_item_get_name (snet);
 +      else if (wave)
 +      string = bse_item_get_name (wave);
 +      else if (sound_font_preset)
 +      string = bse_item_get_name (sound_font_preset);
 +      else
 +      string = "";
 +      g_value_set_string (value, string);
        break;
      case COL_MIDI_CHANNEL:
-       bse_proxy_get (item, "midi-channel", &vint, NULL);
+       bse_proxy_get (item.proxy_id(), "midi-channel", &vint, NULL);
        sfi_value_take_string (value, g_strdup_format ("%2d", vint));
        break;
      case COL_OUTPUTS:
@@@ -294,10 -282,7 +296,10 @@@ track_view_synth_popup (BstTrackVie
                                                              pc->label, pc->tooltip, pc->items,
                                                              _("Available Waves"),
                                                              _("List of available waves to choose a track 
instrument from"),
-                                                             bse_project_get_wave_repo (bse_item_get_project 
(item)),
+                                                             project.get_wave_repo().proxy_id(),
 +                                                          _("Available Sound Fonts"),
 +                                                          _("List of available sound fonts to choose track 
instrument from"),
-                                                           bse_project_get_sound_font_repo 
(bse_item_get_project (item)),
++                                                          project.get_sound_font_repo().proxy_id(),
                                                              track_view_synth_popup_cb, g_memdup (&sdata, 
sizeof (sdata)), track_view_synth_popup_cleanup);
            gxk_cell_renderer_popup_dialog (pcell, dialog);
          }
diff --cc bse/bseapi.idl
index 8154e62,0379f34..9422708
--- a/bse/bseapi.idl
+++ b/bse/bseapi.idl
@@@ -791,8 -808,10 +808,19 @@@ interface Wave : Source 
  
  /// Interface serving as container for Wave objects.
  interface WaveRepo : Super {
+   Error load_file   (String file_name);         ///< Load wave from file.
+   void  remove_wave (Wave wave);                ///< Remove a wave from repository.
+ };
+ 
++/// Interface for sound fonts
++interface SoundFont : Container {
++};
++
++interface SoundFontRepo : Super {
++  Error load_file         (String file_name);         ///< Load sound font from file.
++  Error remove_sound_font (SoundFont sound_font);     ///< Remove a sound font from repository.
 +};
 +
  /// Interface for MIDI event notification.
  interface MidiNotifier : Item {
  };
@@@ -801,31 -820,48 +829,49 @@@
  interface MidiSynth : SNet {
  };
  
+ /// Enumeration describing the current activation and playback state of a project.
+ enum ProjectState {
+   INACTIVE,     ///< The project is not yet hooked to the sound engine.
+   ACTIVE,       ///< The sound engine is activated (rnuning) for this project.
+   PLAYING       ///< The project is active and the sequencer is running.
+ };
+ 
  /// Projects support loading, saving, playback and act as containers for all other sound objects.
  interface Project : Container {
-   void  change_name (String name); ///< Change a project name without recording undo steps.
-   Error play();     ///< Activate a project and start project playback (an already playing project is first 
halted).
-   Error activate(); ///< Activate a project, precondition to start playback.
-   //ProjectState get_state();     ///< Retrieve the current project activation/playback state.
-   bool  can_play();     ///< Check whether project playback would makes sense.
-   bool  is_playing();   ///< Check whether a project is currently playing (song sequencing).
-   bool  is_active ();   ///< Check whether a project is active (currently synthesizing).
-   void  start_playback(); ///< Start playback in an activated project.
-   void  stop_playback(); ///< Stop project playback.
-   void  deactivate();   ///< Deactivate the project, automatically stop playback.
-   void  stop();         ///< Stop project playback and deactivate project.
-   void  auto_deactivate (int32 msec_delay); ///< Automatically deactivate a few milliseconds after playback 
stopped.
-   int32 undo_depth();   ///< Check whether a project can perform undo steps.
-   void  undo();         ///< Undo a previous operation in a project.
-   int32 redo_depth();   ///< Get the number of times redo can be called on the project.
-   void  redo();         ///< Redo a previously undone operation in a project.
-   void  clear_undo();   ///< Delete all recorded undo or redo steps.
-   void  clean_dirty();  ///< Clear a project's dirty flags.
-   bool  is_dirty();     ///< Check whether a project needs saving.
+   signal void  state_changed       (ProjectState newstate); ///< Signal notifies of project state changes.
+   ProjectState get_state           (); ///< Retrieve the current project activation/playback state.
+   void         change_name         (String name); ///< Change a project name without recording undo steps.
+   Error        play                (); ///< Activate a project and start project playback (an already 
playing project is first halted).
+   Error        activate            (); ///< Activate a project, precondition to start playback.
+   bool         can_play            (); ///< Check whether project playback would makes sense.
+   bool         is_playing          (); ///< Check whether a project is currently playing (song sequencing).
+   bool         is_active           (); ///< Check whether a project is active (currently synthesizing).
+   void         start_playback      (); ///< Start playback in an activated project.
+   void         stop_playback       (); ///< Stop project playback.
+   void         deactivate          (); ///< Deactivate the project, automatically stop playback.
+   void         stop                (); ///< Stop project playback and deactivate project.
+   void         auto_deactivate     (int32 msec_delay); ///< Automatically deactivate a few milliseconds 
after playback stopped.
+   int32        undo_depth          (); ///< Check whether a project can perform undo steps.
+   void         undo                (); ///< Undo a previous operation in a project.
+   int32        redo_depth          (); ///< Get the number of times redo can be called on the project.
+   void         redo                (); ///< Redo a previously undone operation in a project.
+   void         clear_undo          (); ///< Delete all recorded undo or redo steps.
+   void         clean_dirty         (); ///< Clear a project's dirty flags.
+   bool         is_dirty            (); ///< Check whether a project needs saving.
+   SuperSeq     get_supers          (); ///< Retrieve all Super type objects of this project.
+   Error        store_bse           (Super super, String file_name, bool self_contained);
+   Song         create_song         (String name); ///< Create a song for this project.
+   WaveRepo     get_wave_repo       ();            ///< Retrieve the project's unique wave repository.
++  SoundFontRepo get_sound_font_repo ();            ///< Retrieve the project's unique sound font repository.
+   CSynth       create_csynth       (String name); ///< Create a synthsizer network for this project.
+   MidiSynth    create_midi_synth   (String name); ///< Create a MIDI synthesizer network for this project.
+   MidiNotifier get_midi_notifier   ();            ///< Retrieve the project's midi notifier object.
+   void         remove_snet         (SNet snet);   ///< Remove an existing synthesizer network from this 
project.
+   Error        restore_from_file   (String file_name); ///< Load a project from file.
    /// Inject a MIDI control event into the project's MIDI receiver.
-   void  inject_midi_control (int32 midi_channel, int32 midi_control, float64 control_value);
+   void         inject_midi_control (int32 midi_channel, int32 midi_control, float64 control_value);
+   Error        import_midi_file    (String file_name); ///< Import a song from a MIDI file.
    //Item    find_item (String uname_path); ///< Find an item within a project, given its uname path.
-   //ItemSeq get_supers(); ///< Retrieve all Super type objects of this project.
    /// List uname paths for all items of a specified type within a project.
    /// By their uname paths, items are uniquely identifyable within a project.
    //StringSeq list_uname_paths (String item_type);
diff --cc bse/bseobject.cc
index 158c451,4df911d..4c635e4
--- a/bse/bseobject.cc
+++ b/bse/bseobject.cc
@@@ -932,6 -917,6 +917,8 @@@ bse_object_new (GType object_type, cons
  #include "bsemidinotifier.hh"
  #include "bsemidisynth.hh"
  #include "bsewaverepo.hh"
++#include "bsesoundfont.hh"
++#include "bsesoundfontrepo.hh"
  #include "bsebus.hh"
  #include "bsesnet.hh"
  #include "bsepart.hh"
@@@ -962,6 -947,6 +949,10 @@@ bse_object_new_valist (GType object_typ
      cxxo = new Bse::WaveImpl (object);
    else if (g_type_is_a (object_type, BSE_TYPE_WAVE_REPO))
      cxxo = new Bse::WaveRepoImpl (object);
++  else if (g_type_is_a (object_type, BSE_TYPE_SOUND_FONT))
++    cxxo = new Bse::SoundFontImpl (object);
++  else if (g_type_is_a (object_type, BSE_TYPE_SOUND_FONT_REPO))
++    cxxo = new Bse::SoundFontRepoImpl (object);
    else if (g_type_is_a (object_type, BSE_TYPE_MIDI_NOTIFIER))
      cxxo = new Bse::MidiNotifierImpl (object);
    else if (g_type_is_a (object_type, BSE_TYPE_MIDI_SYNTH))
diff --cc bse/bseproject.cc
index b38b6ca,ef9d37f..51b9170
--- a/bse/bseproject.cc
+++ b/bse/bseproject.cc
@@@ -1202,4 -1171,142 +1196,150 @@@ ProjectImpl::restore_from_file (const S
    return Bse::Error (error);
  }
  
+ ProjectState
+ ProjectImpl::get_state ()
+ {
+   BseProject *self = as<BseProject*>();
+   return self->state;
+ }
+ 
+ SuperSeq
+ ProjectImpl::get_supers ()
+ {
+   BseProject *self = as<BseProject*>();
+   SuperSeq sseq;
+   for (GSList *slist = self->supers; slist; slist = slist->next)
+     {
+       BseItem *bseitem = (BseItem*) slist->data;
+       sseq.push_back (bseitem->as<SuperIfaceP>());
+     }
+   return sseq;
+ }
+ 
+ void
+ ProjectImpl::remove_snet (SNetIface &snet_iface)
+ {
+   BseProject *self = as<BseProject*>();
+   SNetImpl &snet = dynamic_cast<SNetImpl&> (snet_iface);
+   assert_return (snet.parent() == this);
+   return_unless (BSE_SOURCE_PREPARED (self) == false);
+   BseItem *child = snet.as<BseItem*>();
+   BseUndoStack *ustack = bse_item_undo_open (self, __func__);
+   // backup object references to undo stack
+   bse_container_uncross_undoable (BSE_CONTAINER (self), child);
+   // implement "undo" of bse_container_remove_backedup, i.e. redo
+   UndoDescriptor<SNetImpl> snet_descriptor = undo_descriptor (snet);
+   auto lambda = [snet_descriptor] (ProjectImpl &self, BseUndoStack *ustack) -> Error {
+     SNetImpl &snet = self.undo_resolve (snet_descriptor);
+     self.remove_snet (snet);
+     return Error::NONE;
+   };
+   push_undo_to_redo (__func__, *this, lambda);
+   // backup and remove (without redo queueing)
+   bse_container_remove_backedup (BSE_CONTAINER (self), child, ustack);
+   // done
+   bse_item_undo_close (ustack);
+ }
+ 
+ Error
+ ProjectImpl::store_bse (SuperIface &super_iface, const String &file_name, bool self_contained)
+ {
+   BseProject *self = as<BseProject*>();
+   SuperImpl *super = dynamic_cast<SuperImpl*> (&super_iface);
+   BseSuper *bsesuper = super ? super->as<BseSuper*>() : NULL;
+   if (super)
+     assert_return (super->parent() == this, Error::INTERNAL);
+   return bse_project_store_bse (self, bsesuper, file_name.c_str(), self_contained);
+ }
+ 
+ SongIfaceP
+ ProjectImpl::create_song (const String &name)
+ {
+   BseProject *self = as<BseProject*>();
+   BseUndoStack *ustack = bse_item_undo_open (self, __func__);
+   BseSong *song = (BseSong*) bse_container_new_child (self, BSE_TYPE_SONG, NULL);
+   if (song)
+     {
+       if (!name.empty())
+         bse_item_set (song, "uname", name.c_str(), NULL);
+       UndoDescriptor<SongImpl> song_descriptor = undo_descriptor (*song->as<SongImpl*>());
+       auto remove_song_lambda = [song_descriptor] (ProjectImpl &self, BseUndoStack *ustack) -> Error {
+         SongImpl &song = self.undo_resolve (song_descriptor);
+         self.remove_snet (song);
+         return Error::NONE;
+       };
+       push_undo (__func__, *this, remove_song_lambda);
+     }
+   bse_item_undo_close (ustack);
+   return song->as<SongIfaceP>();
+ }
+ 
+ CSynthIfaceP
+ ProjectImpl::create_csynth (const String &name)
+ {
+   BseProject *self = as<BseProject*>();
+   BseUndoStack *ustack = bse_item_undo_open (self, __func__);
+   BseCSynth *csynth = (BseCSynth*) bse_container_new_child (self, BSE_TYPE_CSYNTH, NULL);
+   if (csynth)
+     {
+       if (!name.empty())
+         bse_item_set (csynth, "uname", name.c_str(), NULL);
+       UndoDescriptor<CSynthImpl> csynth_descriptor = undo_descriptor (*csynth->as<CSynthImpl*>());
+       auto remove_csynth_lambda = [csynth_descriptor] (ProjectImpl &self, BseUndoStack *ustack) -> Error {
+         CSynthImpl &csynth = self.undo_resolve (csynth_descriptor);
+         self.remove_snet (csynth);
+         return Error::NONE;
+       };
+       push_undo (__func__, *this, remove_csynth_lambda);
+     }
+   bse_item_undo_close (ustack);
+   return csynth->as<CSynthIfaceP>();
+ }
+ 
+ MidiSynthIfaceP
+ ProjectImpl::create_midi_synth (const String &name)
+ {
+   BseProject *self = as<BseProject*>();
+   BseUndoStack *ustack = bse_item_undo_open (self, __func__);
+   BseMidiSynth *midi_synth = (BseMidiSynth*) bse_container_new_child (self, BSE_TYPE_MIDI_SYNTH, NULL);
+   if (midi_synth)
+     {
+       if (!name.empty())
+         bse_item_set (midi_synth, "uname", name.c_str(), NULL);
+       UndoDescriptor<MidiSynthImpl> midi_synth_descriptor = undo_descriptor 
(*midi_synth->as<MidiSynthImpl*>());
+       auto remove_midi_synth_lambda = [midi_synth_descriptor] (ProjectImpl &self, BseUndoStack *ustack) -> 
Error {
+         MidiSynthImpl &midi_synth = self.undo_resolve (midi_synth_descriptor);
+         self.remove_snet (midi_synth);
+         return Error::NONE;
+       };
+       push_undo (__func__, *this, remove_midi_synth_lambda);
+     }
+   bse_item_undo_close (ustack);
+   return midi_synth->as<MidiSynthIfaceP>();
+ }
+ 
+ WaveRepoIfaceP
+ ProjectImpl::get_wave_repo ()
+ {
+   BseProject *self = as<BseProject*>();
+   BseWaveRepo *wrepo = bse_project_get_wave_repo (self);
+   return wrepo ? wrepo->as<WaveRepoIfaceP>() : NULL;
+ }
+ 
++SoundFontRepoIfaceP
++ProjectImpl::get_sound_font_repo ()
++{
++  BseProject *self = as<BseProject*>();
++  BseSoundFontRepo *sfrepo = bse_project_get_sound_font_repo (self);
++  return sfrepo ? sfrepo->as<SoundFontRepoIfaceP>() : NULL;
++}
++
+ MidiNotifierIfaceP
+ ProjectImpl::get_midi_notifier ()
+ {
+   BseProject *self = as<BseProject*>();
+   BseMidiNotifier *notifier = bse_project_get_midi_notifier (self);
+   return notifier ? notifier->as<MidiNotifierIfaceP>() : NULL;
+ }
+ 
  } // Bse
diff --cc bse/bseproject.hh
index aaa8fa8,2d6aa28..7702d86
--- a/bse/bseproject.hh
+++ b/bse/bseproject.hh
@@@ -80,30 -72,39 +73,40 @@@ namespace Bse 
  
  class ProjectImpl : public ContainerImpl, public virtual ProjectIface {
  protected:
-   virtual          ~ProjectImpl ();
+   virtual                   ~ProjectImpl         ();
  public:
-   explicit          ProjectImpl         (BseObject*);
-   virtual void      change_name         (const String &name) override;
-   virtual Error play                () override;
-   virtual Error activate            () override;
-   virtual bool      can_play            () override;
-   virtual bool      is_playing          () override;
-   virtual bool      is_active           () override;
-   virtual void      start_playback      () override;
-   virtual void      stop_playback       () override;
-   virtual void      deactivate          () override;
-   virtual void      stop                () override;
-   virtual void      auto_deactivate     (int msec_delay) override;
-   virtual int       undo_depth          () override;
-   virtual void      undo                () override;
-   virtual int       redo_depth          () override;
-   virtual void      redo                () override;
-   virtual void      clear_undo          () override;
-   virtual void      clean_dirty         () override;
-   virtual bool      is_dirty            () override;
-   virtual void      inject_midi_control (int midi_channel, int midi_control, double control_value) override;
-   virtual Error import_midi_file    (const String &file_name) override;
-   virtual Error restore_from_file   (const String &file_name) override;
+   explicit                   ProjectImpl         (BseObject*);
+   virtual void               change_name         (const String &name) override;
+   virtual Error              play                () override;
+   virtual Error              activate            () override;
+   virtual bool               can_play            () override;
+   virtual bool               is_playing          () override;
+   virtual bool               is_active           () override;
+   virtual void               start_playback      () override;
+   virtual void               stop_playback       () override;
+   virtual void               deactivate          () override;
+   virtual void               stop                () override;
+   virtual void               auto_deactivate     (int msec_delay) override;
+   virtual int                undo_depth          () override;
+   virtual void               undo                () override;
+   virtual int                redo_depth          () override;
+   virtual void               redo                () override;
+   virtual void               clear_undo          () override;
+   virtual void               clean_dirty         () override;
+   virtual bool               is_dirty            () override;
+   virtual void               inject_midi_control (int midi_channel, int midi_control, double control_value) 
override;
+   virtual Error              import_midi_file    (const String &file_name) override;
+   virtual Error              restore_from_file   (const String &file_name) override;
+   virtual ProjectState       get_state           () override;
+   virtual SuperSeq           get_supers          () override;
+   virtual Error              store_bse           (SuperIface &super, const String &file_name, bool 
self_contained) override;
+   virtual SongIfaceP         create_song         (const String &name) override;
+   virtual WaveRepoIfaceP     get_wave_repo       () override;
++  virtual SoundFontRepoIfaceP get_sound_font_repo () override;
+   virtual CSynthIfaceP       create_csynth       (const String &name) override;
+   virtual MidiSynthIfaceP    create_midi_synth   (const String &name) override;
+   virtual MidiNotifierIfaceP get_midi_notifier   () override;
+   virtual void               remove_snet         (SNetIface &snet) override;
  };
  
  } // Bse
diff --cc bse/bsesoundfont.cc
index 199e089,0000000..e9893ba
mode 100644,000000..100644
--- a/bse/bsesoundfont.cc
+++ b/bse/bsesoundfont.cc
@@@ -1,379 -1,0 +1,390 @@@
 +// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 +#include "bsesoundfont.hh"
 +#include "bsesoundfontrepo.hh"
 +#include "bsesoundfontpreset.hh"
 +#include "bsemain.hh"
 +#include "bsestorage.hh"
 +#include "bseprocedure.hh"
 +#include "gsldatahandle.hh"
 +#include "bseserver.hh"
 +#include "bseloader.hh"
 +
 +#include <string.h>
 +
 +#define parse_or_return         bse_storage_scanner_parse_or_return
 +
 +enum {
 +  PARAM_0,
 +  PARAM_FILE_NAME,
 +};
 +
 +/* --- prototypes --- */
 +
 +
 +/* --- variables --- */
 +static void       *parent_class = NULL;
 +static GQuark      quark_load_sound_font = 0;
 +
 +
 +/* --- functions --- */
 +static void
 +bse_sound_font_init (BseSoundFont *sound_font)
 +{
 +  sound_font->blob = NULL;
 +  sound_font->sfont_id = -1;
 +  sound_font->sfrepo = NULL;
 +}
 +
 +static void
 +bse_sound_font_set_property (GObject      *object,
 +                           guint         param_id,
 +                           const GValue *value,
 +                           GParamSpec   *pspec)
 +{
 +  switch (param_id)
 +    {
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 +      break;
 +    }
 +}
 +
 +static void
 +bse_sound_font_get_property (GObject    *object,
 +                           guint       param_id,
 +                           GValue     *value,
 +                           GParamSpec *pspec)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (object);
 +  switch (param_id)
 +    {
 +    case PARAM_FILE_NAME:
 +      if (sound_font->blob)
 +        sfi_value_set_string (value, bse_storage_blob_file_name (sound_font->blob));
 +      else
 +        sfi_value_set_string (value, NULL);
 +      break;
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
 +      break;
 +    }
 +}
 +
 +static void
 +bse_sound_font_dispose (GObject *object)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (object);
 +  if (sound_font->sfont_id != -1)
 +    bse_sound_font_unload (sound_font);
 +  if (sound_font->sfrepo)
 +    {
 +      g_object_unref (sound_font->sfrepo);
 +      sound_font->sfrepo = NULL;
 +    }
 +  /* chain parent class' handler */
 +  G_OBJECT_CLASS (parent_class)->dispose (object);
 +}
 +
 +static void
 +bse_sound_font_finalize (GObject *object)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (object);
 +
 +  /* free blob */
 +  if (sound_font->blob)
 +    {
 +      bse_storage_blob_unref (sound_font->blob);
 +      sound_font->blob = NULL;
 +    }
 +
 +  if (sound_font->sfrepo != NULL || sound_font->blob != NULL || sound_font->sfont_id != -1)
 +    g_warning (G_STRLOC ": some resources could not be freed.");
 +
 +  /* chain parent class' handler */
 +  G_OBJECT_CLASS (parent_class)->finalize (object);
 +}
 +
 +Bse::Error
 +bse_sound_font_load_blob (BseSoundFont    *self,
 +                          BseStorageBlob  *blob,
 +                        gboolean         init_presets)
 +{
 +  if (self->sfrepo == NULL)
 +    {
 +      self->sfrepo = BSE_SOUND_FONT_REPO (BSE_ITEM (self)->parent);
 +      g_object_ref (self->sfrepo);
 +    }
 +
 +  g_return_val_if_fail (blob != NULL, Bse::Error::INTERNAL);
 +  g_return_val_if_fail (self->sfrepo != NULL, Bse::Error::INTERNAL);
 +  g_return_val_if_fail (self->sfont_id == -1, Bse::Error::INTERNAL);
 +
 +  bse_storage_blob_ref (blob);
 +  if (self->blob)
 +    {
 +      bse_storage_blob_unref (self->blob);
 +      self->blob = NULL;
 +    }
 +
 +  fluid_synth_t *fluid_synth = bse_sound_font_repo_lock_fluid_synth (self->sfrepo);
 +  int sfont_id = fluid_synth_sfload (fluid_synth, bse_storage_blob_file_name (blob), 0);
 +  Bse::Error error;
 +  if (sfont_id != -1)
 +    {
 +      if (init_presets)
 +      {
 +        fluid_sfont_t *fluid_sfont = fluid_synth_get_sfont_by_id (fluid_synth, sfont_id);
 +        fluid_preset_t fluid_preset;
 +
 +        fluid_sfont->iteration_start (fluid_sfont);
 +        while (fluid_sfont->iteration_next (fluid_sfont, &fluid_preset))
 +          {
 +            BseSoundFontPreset *sound_font_preset;
 +              sound_font_preset = (BseSoundFontPreset *) bse_object_new (BSE_TYPE_SOUND_FONT_PRESET,
 +                                                                       "uname", fluid_preset.get_name 
(&fluid_preset),
 +                                                                       NULL);
 +            bse_container_add_item (BSE_CONTAINER (self), BSE_ITEM (sound_font_preset));
 +            bse_sound_font_preset_init_preset (sound_font_preset, &fluid_preset);
 +          }
 +      }
 +      self->sfont_id = sfont_id;
 +      self->blob = blob;
 +      error = Bse::Error::NONE;
 +    }
 +  else
 +    {
 +      bse_storage_blob_unref (blob);
 +      error = Bse::Error::WAVE_NOT_FOUND;
 +    }
 +  bse_sound_font_repo_unlock_fluid_synth (self->sfrepo);
 +  return error;
 +}
 +
 +void
 +bse_sound_font_unload (BseSoundFont *sound_font)
 +{
 +  g_return_if_fail (sound_font->sfrepo != NULL);
 +
 +  if (sound_font->sfont_id != -1)
 +    {
 +      fluid_synth_t *fluid_synth = bse_sound_font_repo_lock_fluid_synth (sound_font->sfrepo);
 +      fluid_synth_sfunload (fluid_synth, sound_font->sfont_id, 1 /* reset presets */);
 +      bse_sound_font_repo_unlock_fluid_synth (sound_font->sfrepo);
 +    }
 +  sound_font->sfont_id = -1;
 +}
 +
 +Bse::Error
 +bse_sound_font_reload (BseSoundFont *sound_font)
 +{
 +  g_return_val_if_fail (sound_font->sfont_id == -1, Bse::Error::INTERNAL);
 +
 +  return bse_sound_font_load_blob (sound_font, sound_font->blob, FALSE);
 +}
 +
 +static void
 +bse_sound_font_store_private (BseObject  *object,
 +                            BseStorage *storage)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (object);
 +  /* chain parent class' handler */
 +  BSE_OBJECT_CLASS (parent_class)->store_private (object, storage);
 +
 +  if (!BSE_STORAGE_SELF_CONTAINED (storage) && !bse_storage_blob_is_temp_file (sound_font->blob))
 +    {
 +      bse_storage_break (storage);
 +      bse_storage_printf (storage, "(load-sound-font \"%s\")", bse_storage_blob_file_name 
(sound_font->blob));
 +    }
 +  else
 +    {
 +      bse_storage_break (storage);
 +      bse_storage_printf (storage, "(load-sound-font ");
 +      bse_storage_put_blob (storage, sound_font->blob);
 +      bse_storage_printf (storage, ")");
 +    }
 +}
 +
 +static GTokenType
 +bse_sound_font_restore_private (BseObject  *object,
 +                              BseStorage *storage,
 +                                GScanner   *scanner)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (object);
 +  GTokenType expected_token;
 +  GQuark quark;
 +
 +  /* chain parent class' handler */
 +  if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
 +    return BSE_OBJECT_CLASS (parent_class)->restore_private (object, storage, scanner);
 +
 +  /* parse storage commands */
 +  quark = g_quark_try_string (scanner->next_value.v_identifier);
 +  if (quark == quark_load_sound_font)
 +    {
 +      BseStorageBlob *blob;
 +      Bse::Error error;
 +
 +      g_scanner_get_next_token (scanner); /* eat quark identifier */
 +      if (g_scanner_peek_next_token (scanner) == G_TOKEN_STRING)
 +      {
 +        parse_or_return (scanner, G_TOKEN_STRING);
 +        blob = bse_storage_blob_new_from_file (scanner->value.v_string, FALSE);
 +      }
 +      else
 +      {
 +          GTokenType token = bse_storage_parse_blob (storage, &blob);
 +        if (token != G_TOKEN_NONE)
 +          {
 +            if (blob)
 +              bse_storage_blob_unref (blob);
 +              return token;
 +          }
 +      }
 +      if (g_scanner_peek_next_token (scanner) != ')')
 +      {
 +        bse_storage_blob_unref (blob);
 +        return GTokenType (')');
 +      }
 +      parse_or_return (scanner, ')');
 +      error = bse_sound_font_load_blob (sound_font, blob, FALSE);
 +      if (error != 0)
 +      bse_storage_warn (storage, "failed to load sound font \"%s\": %s",
 +                                  bse_storage_blob_file_name (blob), bse_error_blurb (error));
 +      bse_storage_blob_unref (blob);
 +      expected_token = G_TOKEN_NONE; /* got ')' */
 +    }
 +  else /* chain parent class' handler */
 +    expected_token = BSE_OBJECT_CLASS (parent_class)->restore_private (object, storage, scanner);
 +
 +  return expected_token;
 +}
 +
 +
 +static void
 +bse_sound_font_add_item (BseContainer *container,
 +                       BseItem      *item)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (container);
 +
 +  if (g_type_is_a (BSE_OBJECT_TYPE (item), BSE_TYPE_SOUND_FONT_PRESET))
 +    sound_font->presets = g_list_append (sound_font->presets, item);
 +  else
 +    g_warning ("BseSoundFont: cannot hold non-sound-font-preset item type `%s'",
 +             BSE_OBJECT_TYPE_NAME (item));
 +
 +  /* chain parent class' add_item handler */
 +  BSE_CONTAINER_CLASS (parent_class)->add_item (container, item);
 +}
 +
 +static void
 +bse_sound_font_forall_items (BseContainer      *container,
 +                           BseForallItemsFunc func,
 +                           gpointer           data)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (container);
 +  GList *list;
 +
 +  list = sound_font->presets;
 +  while (list)
 +    {
 +      BseItem *item;
 +
 +      item = BSE_ITEM (list->data);
 +      list = list->next;
 +      if (!func (item, data))
 +      return;
 +    }
 +}
 +
 +static void
 +bse_sound_font_remove_item (BseContainer *container,
 +                          BseItem      *item)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (container);
 +
 +  if (g_type_is_a (BSE_OBJECT_TYPE (item), BSE_TYPE_SOUND_FONT_PRESET))
 +    sound_font->presets = g_list_remove (sound_font->presets, item);
 +  else
 +    g_warning ("BseSoundFontRepo: cannot hold non-sound-font-preset item type `%s'",
 +             BSE_OBJECT_TYPE_NAME (item));
 +
 +  /* chain parent class' remove_item handler */
 +  BSE_CONTAINER_CLASS (parent_class)->remove_item (container, item);
 +}
 +
 +static void
 +bse_sound_font_release_children (BseContainer *container)
 +{
 +  BseSoundFont *self = BSE_SOUND_FONT (container);
 +
 +  while (self->presets)
 +    bse_container_remove_item (container, BSE_ITEM (self->presets->data));
 +
 +  /* chain parent class' handler */
 +  BSE_CONTAINER_CLASS (parent_class)->release_children (container);
 +}
 +
 +
 +static void
 +bse_sound_font_class_init (BseSoundFontClass *klass)
 +{
 +  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 +  BseObjectClass *object_class = BSE_OBJECT_CLASS (klass);
 +  BseContainerClass *container_class = BSE_CONTAINER_CLASS (klass);
 +
 +  parent_class = g_type_class_peek_parent (klass);
 +
 +  gobject_class->set_property = bse_sound_font_set_property;
 +  gobject_class->get_property = bse_sound_font_get_property;
 +  gobject_class->dispose = bse_sound_font_dispose;
 +  gobject_class->finalize = bse_sound_font_finalize;
 +
 +  container_class->add_item = bse_sound_font_add_item;
 +  container_class->remove_item = bse_sound_font_remove_item;
 +  container_class->forall_items = bse_sound_font_forall_items;
 +  container_class->release_children = bse_sound_font_release_children;
 +
 +  object_class->store_private = bse_sound_font_store_private;
 +  object_class->restore_private = bse_sound_font_restore_private;
 +
 +  quark_load_sound_font = g_quark_from_static_string ("load-sound-font");
 +
 +  bse_object_class_add_param (object_class, "Locator",
 +                            PARAM_FILE_NAME,
 +                            sfi_pspec_string ("file_name", "File Name", NULL,
 +                                              NULL, "G:r"));
 +}
 +
 +BSE_BUILTIN_TYPE (BseSoundFont)
 +{
 +  static const GTypeInfo sound_font_info = {
 +    sizeof (BseSoundFontClass),
 +
 +    (GBaseInitFunc) NULL,
 +    (GBaseFinalizeFunc) NULL,
 +    (GClassInitFunc) bse_sound_font_class_init,
 +    (GClassFinalizeFunc) NULL,
 +    NULL /* class_data */,
 +
 +    sizeof (BseSoundFont),
 +    0  /* n_preallocs */,
 +    (GInstanceInitFunc) bse_sound_font_init,
 +  };
 +
 +  return bse_type_register_static (BSE_TYPE_CONTAINER,
 +                                 "BseSoundFont",
 +                                 "BSE sound_font type",
 +                                   __FILE__, __LINE__,
 +                                   &sound_font_info);
 +}
++
++namespace Bse {
++
++SoundFontImpl::SoundFontImpl (BseObject *bobj) :
++  ContainerImpl (bobj)
++{}
++
++SoundFontImpl::~SoundFontImpl ()
++{}
++
++} // Bse
diff --cc bse/bsesoundfont.hh
index ab0784b,0000000..254cf20
mode 100644,000000..100644
--- a/bse/bsesoundfont.hh
+++ b/bse/bsesoundfont.hh
@@@ -1,35 -1,0 +1,47 @@@
 +// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 +#ifndef __BSE_SOUND_FONT_HH__
 +#define __BSE_SOUND_FONT_HH__
 +
 +#include      <bse/bsecontainer.hh>
 +#include        <bse/bsestorage.hh>
 +
 +G_BEGIN_DECLS
 +
 +/* --- BSE type macros --- */
 +#define BSE_TYPE_SOUND_FONT             (BSE_TYPE_ID (BseSoundFont))
 +#define BSE_SOUND_FONT(object)                  (G_TYPE_CHECK_INSTANCE_CAST ((object), BSE_TYPE_SOUND_FONT, 
BseSoundFont))
 +#define BSE_SOUND_FONT_CLASS(class)     (G_TYPE_CHECK_CLASS_CAST ((class), BSE_TYPE_SOUND_FONT, 
BseSoundFontClass))
 +#define BSE_IS_SOUND_FONT(object)       (G_TYPE_CHECK_INSTANCE_TYPE ((object), BSE_TYPE_SOUND_FONT))
 +#define BSE_IS_SOUND_FONT_CLASS(class)          (G_TYPE_CHECK_CLASS_TYPE ((class), BSE_TYPE_SOUND_FONT))
 +#define BSE_SOUND_FONT_GET_CLASS(object)  (G_TYPE_INSTANCE_GET_CLASS ((object), BSE_TYPE_SOUND_FONT, 
BseSoundFontClass))
 +
 +struct BseSoundFont : BseContainer {
 +  BseStorageBlob    *blob;
 +  int                sfont_id;
 +  BseSoundFontRepo  *sfrepo;
 +  GList             *presets;
 +};
 +struct BseSoundFontClass : BseContainerClass
 +{};
 +
 +Bse::Error      bse_sound_font_load_blob      (BseSoundFont       *sound_font,
 +                                               BseStorageBlob     *blob,
 +                                               gboolean            init_presets);
 +void          bse_sound_font_unload           (BseSoundFont       *sound_font);
 +Bse::Error      bse_sound_font_reload           (BseSoundFont       *sound_font);
 +
 +G_END_DECLS
 +
++namespace Bse {
++
++class SoundFontImpl : public ContainerImpl, public virtual SoundFontIface {
++protected:
++  virtual  ~SoundFontImpl ();
++public:
++  explicit  SoundFontImpl (BseObject*);
++};
++
++} // Bse
++
++
 +#endif /* __BSE_SOUND_FONT_HH__ */
diff --cc bse/bsesoundfontrepo.cc
index 4ee9d0a,0000000..dd7d6f3
mode 100644,000000..100644
--- a/bse/bsesoundfontrepo.cc
+++ b/bse/bsesoundfontrepo.cc
@@@ -1,389 -1,0 +1,490 @@@
 +// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 +#include        "bsesoundfontrepo.hh"
 +#include        "bsesoundfont.hh"
 +#include        "bsesoundfontpreset.hh"
 +#include        "bsedefs.hh"
 +#include        "bseblockutils.hh"
 +
 +
 +/* --- parameters --- */
 +enum
 +{
 +  PARAM_0,
 +};
 +
 +
 +/* --- prototypes --- */
 +static void   bse_sound_font_repo_class_init          (BseSoundFontRepoClass  *klass);
 +static void   bse_sound_font_repo_init                (BseSoundFontRepo       *wrepo);
 +static void   bse_sound_font_repo_dispose             (GObject                *object);
 +static void     bse_sound_font_repo_release_children    (BseContainer         *container);
 +static void   bse_sound_font_repo_set_property        (GObject                *object,
 +                                                       guint                   param_id,
 +                                                       const GValue           *value,
 +                                                       GParamSpec             *pspec);
 +static void   bse_sound_font_repo_get_property (GObject               *object,
 +                                                guint                  param_id,
 +                                                GValue                *value,
 +                                                GParamSpec            *pspec);
 +static void     bse_sound_font_repo_add_item     (BseContainer                *container,
 +                                                BseItem               *item);
 +static void     bse_sound_font_repo_forall_items (BseContainer                *container,
 +                                                BseForallItemsFunc     func,
 +                                                gpointer               data);
 +static void     bse_sound_font_repo_remove_item        (BseContainer          *container,
 +                                                BseItem               *item);
 +static void     bse_sound_font_repo_prepare      (BseSource             *source);
 +
 +
 +/* --- variables --- */
 +static gpointer parent_class = NULL;
 +
 +
 +/* --- functions --- */
 +BSE_BUILTIN_TYPE (BseSoundFontRepo)
 +{
 +  GType sound_font_repo_type;
 +
 +  static const GTypeInfo sfrepo_info = {
 +    sizeof (BseSoundFontRepoClass),
 +
 +    (GBaseInitFunc) NULL,
 +    (GBaseFinalizeFunc) NULL,
 +    (GClassInitFunc) bse_sound_font_repo_class_init,
 +    (GClassFinalizeFunc) NULL,
 +    NULL /* class_data */,
 +
 +    sizeof (BseSoundFontRepo),
 +    0,
 +    (GInstanceInitFunc) bse_sound_font_repo_init,
 +  };
 +
 +  sound_font_repo_type = bse_type_register_static (BSE_TYPE_SUPER,
 +                                                 "BseSoundFontRepo",
 +                                                 "BSE Sound Font Repository",
 +                                                   __FILE__, __LINE__,
 +                                                   &sfrepo_info);
 +  return sound_font_repo_type;
 +}
 +
 +static void
 +bse_sound_font_repo_finalize (GObject *object)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (object);
 +  G_OBJECT_CLASS (parent_class)->finalize (object);
 +  sfrepo->fluid_synth_mutex.~Mutex();
 +}
 +
 +static void
 +bse_sound_font_repo_class_init (BseSoundFontRepoClass *klass)
 +{
 +  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 +  BseContainerClass *container_class = BSE_CONTAINER_CLASS (klass);
 +  BseSourceClass *source_class = BSE_SOURCE_CLASS (klass);
 +
 +  parent_class = g_type_class_peek_parent (klass);
 +
 +  gobject_class->set_property = bse_sound_font_repo_set_property;
 +  gobject_class->get_property = bse_sound_font_repo_get_property;
 +  gobject_class->dispose = bse_sound_font_repo_dispose;
 +  gobject_class->finalize = bse_sound_font_repo_finalize;
 +
 +  container_class->add_item = bse_sound_font_repo_add_item;
 +  container_class->remove_item = bse_sound_font_repo_remove_item;
 +  container_class->forall_items = bse_sound_font_repo_forall_items;
 +  container_class->release_children = bse_sound_font_repo_release_children;
 +
 +  source_class->prepare = bse_sound_font_repo_prepare;
 +}
 +
 +static void
 +bse_sound_font_repo_init (BseSoundFontRepo *sfrepo)
 +{
 +  new (&sfrepo->fluid_synth_mutex) Bse::Mutex();
 +
 +  sfrepo->n_oscs = 0;
 +  sfrepo->oscs = NULL;
 +  sfrepo->channel_map = NULL;
 +
 +  sfrepo->fluid_settings = new_fluid_settings();
 +  sfrepo->fluid_synth = new_fluid_synth (sfrepo->fluid_settings);
 +  sfrepo->fluid_events = NULL;
 +  sfrepo->sound_fonts = NULL;
 +  sfrepo->fluid_mix_freq = 0;
 +
 +  sfrepo->n_fluid_channels = 0;
 +  sfrepo->channel_values_left = NULL;
 +  sfrepo->channel_values_right = NULL;
 +  sfrepo->n_silence_samples = NULL;
 +
 +  sfrepo->n_channel_oscs_active = 0;
 +  sfrepo->channel_values_tick_stamp = 0;
 +}
 +
 +static gboolean
 +reload_sound_font (BseItem *item,
 +                   gpointer data)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (item);
 +  bse_sound_font_reload (sound_font);
 +  return TRUE;
 +}
 +
 +static gboolean
 +unload_sound_font (BseItem *item,
 +                   gpointer data)
 +{
 +  BseSoundFont *sound_font = BSE_SOUND_FONT (item);
 +  bse_sound_font_unload (sound_font);
 +  return TRUE;
 +}
 +
 +static void
 +bse_sound_font_repo_prepare (BseSource *source)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (source);
 +  int i, channels_required = 0;
 +  for (i = 0; i < sfrepo->n_oscs; i++)
 +    {
 +      if (sfrepo->oscs[i])
 +      sfrepo->channel_map[i] = channels_required++;
 +    }
 +  int mix_freq = bse_engine_sample_freq();
 +  if (sfrepo->n_fluid_channels != channels_required || sfrepo->fluid_mix_freq != mix_freq)
 +    {
 +      for (i = channels_required; i < sfrepo->n_fluid_channels; i++) // n_fluid_channels > channels_required
 +      {
 +        g_free (sfrepo->channel_values_left[i]);
 +        g_free (sfrepo->channel_values_right[i]);
 +      }
 +      sfrepo->channel_values_left = (float **)g_realloc (sfrepo->channel_values_left, sizeof (float *) * 
channels_required);
 +      sfrepo->channel_values_right = (float **)g_realloc (sfrepo->channel_values_right, sizeof (float *) * 
channels_required);
 +      sfrepo->n_silence_samples = (gint *) g_realloc (sfrepo->n_silence_samples, sizeof (gint) * 
channels_required);
 +      for (i = sfrepo->n_fluid_channels; i < channels_required; i++) // n_fluid_channels < channels_required
 +      {
 +        sfrepo->channel_values_left[i] = g_new0 (float, BSE_STREAM_MAX_VALUES);
 +        sfrepo->channel_values_right[i] = g_new0 (float, BSE_STREAM_MAX_VALUES);
 +        sfrepo->n_silence_samples[i] = 0;
 +      }
 +      sfrepo->n_fluid_channels = channels_required;
 +      sfrepo->fluid_mix_freq = mix_freq;
 +
 +      fluid_settings_setnum (sfrepo->fluid_settings, "synth.sample-rate", mix_freq);
 +      /* soundfont instruments should be as loud as beast synthesis network instruments */
 +      fluid_settings_setnum (sfrepo->fluid_settings, "synth.gain", 1.0);
 +      fluid_settings_setint (sfrepo->fluid_settings, "synth.midi-channels", channels_required);
 +      fluid_settings_setint (sfrepo->fluid_settings, "synth.audio-channels", channels_required);
 +      fluid_settings_setint (sfrepo->fluid_settings, "synth.audio-groups", channels_required);
 +      fluid_settings_setstr (sfrepo->fluid_settings, "synth.reverb.active", "no");
 +      fluid_settings_setstr (sfrepo->fluid_settings, "synth.chorus.active", "no");
 +
 +      bse_sound_font_repo_forall_items (BSE_CONTAINER (sfrepo), unload_sound_font, sfrepo);
 +      if (sfrepo->fluid_synth)
 +      delete_fluid_synth (sfrepo->fluid_synth);
 +
 +      sfrepo->fluid_synth = new_fluid_synth (sfrepo->fluid_settings);
 +      bse_sound_font_repo_forall_items (BSE_CONTAINER (sfrepo), reload_sound_font, sfrepo);
 +    }
 +
 +  /* chain parent class' handler */
 +  BSE_SOURCE_CLASS (parent_class)->prepare (source);
 +}
 +
 +static void
 +bse_sound_font_repo_release_children (BseContainer *container)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (container);
 +
 +  while (sfrepo->sound_fonts)
 +    bse_container_remove_item (container, BSE_ITEM (sfrepo->sound_fonts->data));
 +
 +  /* chain parent class' handler */
 +  BSE_CONTAINER_CLASS (parent_class)->release_children (container);
 +}
 +
 +static void
 +bse_sound_font_repo_dispose (GObject *object)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (object);
 +  int i;
 +
 +  bse_sound_font_repo_forall_items (BSE_CONTAINER (sfrepo), unload_sound_font, sfrepo);
 +
 +  if (sfrepo->fluid_synth)
 +    {
 +      delete_fluid_synth (sfrepo->fluid_synth);
 +      sfrepo->fluid_synth = NULL;
 +    }
 +  if (sfrepo->fluid_settings)
 +    {
 +      delete_fluid_settings (sfrepo->fluid_settings);
 +      sfrepo->fluid_settings = NULL;
 +    }
 +  g_free (sfrepo->channel_map);
 +  sfrepo->channel_map = NULL;
 +  g_free (sfrepo->oscs);
 +  sfrepo->oscs = NULL;
 +  for (i = 0; i < sfrepo->n_fluid_channels; i++)
 +    {
 +      g_free (sfrepo->channel_values_left[i]);
 +      g_free (sfrepo->channel_values_right[i]);
 +    }
 +  sfrepo->n_fluid_channels = 0;
 +  g_free (sfrepo->channel_values_left);
 +  sfrepo->channel_values_left = NULL;
 +  g_free (sfrepo->channel_values_right);
 +  sfrepo->channel_values_right = NULL;
 +  g_free (sfrepo->n_silence_samples);
 +  sfrepo->n_silence_samples = NULL;
 +
 +  if (sfrepo->fluid_events != NULL)
 +    g_warning (G_STRLOC ": fluid event queue should be empty in dispose");
 +
 +  /* chain parent class' handler */
 +  G_OBJECT_CLASS (parent_class)->dispose (object);
 +}
 +
 +static void
 +bse_sound_font_repo_set_property (GObject      *object,
 +                                guint         param_id,
 +                                const GValue *value,
 +                                GParamSpec   *pspec)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (object);
 +  switch (param_id)
 +    {
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (sfrepo, param_id, pspec);
 +      break;
 +    }
 +}
 +
 +static void
 +bse_sound_font_repo_get_property (GObject      *object,
 +                                guint         param_id,
 +                                GValue       *value,
 +                                GParamSpec   *pspec)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (object);
 +  switch (param_id)
 +    {
 +    default:
 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (sfrepo, param_id, pspec);
 +      break;
 +    }
 +}
 +
 +static void
 +bse_sound_font_repo_add_item (BseContainer *container,
 +                            BseItem      *item)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (container);
 +
 +  if (g_type_is_a (BSE_OBJECT_TYPE (item), BSE_TYPE_SOUND_FONT))
 +    sfrepo->sound_fonts = g_list_append (sfrepo->sound_fonts, item);
 +  else
 +    g_warning ("BseSoundFontRepo: cannot hold non-sound-font item type `%s'",
 +             BSE_OBJECT_TYPE_NAME (item));
 +
 +  /* chain parent class' add_item handler */
 +  BSE_CONTAINER_CLASS (parent_class)->add_item (container, item);
 +}
 +
 +static void
 +bse_sound_font_repo_forall_items (BseContainer      *container,
 +                                BseForallItemsFunc func,
 +                                gpointer           data)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (container);
 +  GList *list;
 +
 +  list = sfrepo->sound_fonts;
 +  while (list)
 +    {
 +      BseItem *item;
 +
 +      item = BSE_ITEM (list->data);
 +      list = list->next;
 +      if (!func (item, data))
 +      return;
 +    }
 +}
 +
 +static void
 +bse_sound_font_repo_remove_item (BseContainer *container,
 +                               BseItem      *item)
 +{
 +  BseSoundFontRepo *sfrepo = BSE_SOUND_FONT_REPO (container);
 +
 +  if (g_type_is_a (BSE_OBJECT_TYPE (item), BSE_TYPE_SOUND_FONT))
 +    sfrepo->sound_fonts = g_list_remove (sfrepo->sound_fonts, item);
 +  else
 +    g_warning ("BseSoundFontRepo: cannot hold non-sound-font item type `%s'",
 +             BSE_OBJECT_TYPE_NAME (item));
 +
 +  /* chain parent class' remove_item handler */
 +  BSE_CONTAINER_CLASS (parent_class)->remove_item (container, item);
 +}
 +
 +static gboolean
 +gather_presets (BseItem    *item,
 +                void       *pitems)
 +{
 +  BseIt3mSeq *items = (BseIt3mSeq *) pitems;
 +  if (BSE_IS_SOUND_FONT (item) || BSE_IS_SOUND_FONT_REPO (item))
 +    bse_container_forall_items (BSE_CONTAINER (item), gather_presets, items);
 +  else if (BSE_IS_SOUND_FONT_PRESET (item))
 +    bse_it3m_seq_append (items, item);
 +  else
 +    g_warning ("Searching for sound font presets, an unexpected `%s' item was found", BSE_OBJECT_TYPE_NAME 
(item));
 +  return TRUE;
 +}
 +
 +void
 +bse_sound_font_repo_list_all_presets (BseSoundFontRepo *sfrepo,
 +                                      BseIt3mSeq       *items)
 +{
 +  gather_presets (BSE_ITEM (sfrepo), items);
 +}
 +
 +fluid_synth_t *
 +bse_sound_font_repo_lock_fluid_synth (BseSoundFontRepo *sfrepo)
 +{
 +  sfrepo->fluid_synth_mutex.lock();
 +  return sfrepo->fluid_synth;
 +}
 +
 +void
 +bse_sound_font_repo_unlock_fluid_synth (BseSoundFontRepo *sfrepo)
 +{
 +  sfrepo->fluid_synth_mutex.unlock();
 +}
 +
 +int
 +bse_sound_font_repo_add_osc (BseSoundFontRepo *sfrepo,
 +                             BseSoundFontOsc  *osc)
 +{
 +  int i;
 +  for (i = 0; i < sfrepo->n_oscs; i++)
 +    {
 +      if (sfrepo->oscs[i] == 0)
 +      {
 +        sfrepo->oscs[i] = osc;
 +        return i;
 +      }
 +    }
 +  sfrepo->oscs = (BseSoundFontOsc **)g_realloc (sfrepo->oscs, sizeof (BseSoundFontOsc *) * (i + 1));
 +  sfrepo->oscs[i] = osc;
 +  sfrepo->channel_map = (guint *) g_realloc (sfrepo->channel_map, sizeof (guint) * (i + 1));
 +  return sfrepo->n_oscs++;
 +}
 +
 +void
 +bse_sound_font_repo_remove_osc (BseSoundFontRepo *sfrepo,
 +                                int               osc_id)
 +{
 +  g_return_if_fail (osc_id >= 0 && osc_id < sfrepo->n_oscs);
 +
 +  sfrepo->oscs[osc_id] = 0;
 +}
++
++namespace Bse {
++
++SoundFontRepoImpl::SoundFontRepoImpl (BseObject *bobj) :
++  SuperImpl (bobj)
++{}
++
++SoundFontRepoImpl::~SoundFontRepoImpl ()
++{}
++
++static Error
++repo_load_file (BseSoundFontRepo *sfrepo, const String &file_name, BseSoundFont **sound_font_p)
++{
++  String fname = Path::basename (file_name);
++  BseSoundFont *sound_font = (BseSoundFont*) bse_object_new (BSE_TYPE_SOUND_FONT, "uname", fname.c_str(), 
NULL);
++  bse_container_add_item (BSE_CONTAINER (sfrepo), BSE_ITEM (sound_font));
++
++  BseStorageBlob *blob = bse_storage_blob_new_from_file (file_name.c_str(), FALSE);
++  Error error = bse_sound_font_load_blob (sound_font, blob, TRUE);
++  bse_storage_blob_unref (blob);
++
++  if (error == Bse::Error::NONE)
++    {
++      *sound_font_p = sound_font;
++      error = Error::NONE;
++    }
++  else
++    {
++      bse_container_remove_item (BSE_CONTAINER (sfrepo), BSE_ITEM (sound_font));
++      *sound_font_p = NULL;
++    }
++  g_object_unref (sound_font);
++  return error;
++}
++
++
++Error
++SoundFontRepoImpl::load_file (const String &file_name)
++{
++  BseSoundFontRepo *self = as<BseSoundFontRepo*>();
++
++  if (BSE_SOURCE_PREPARED (self))
++    {
++      /* In theory, its possible to allow loading sound fonts while
++       * the project is playing; in practice, the sound font repo
++       * lock would be locked for a very long time, which would stall
++       * the audio production ...
++       */
++      return Bse::Error::SOURCE_BUSY;
++    }
++
++  BseSoundFont *sound_font = NULL;
++  Bse::Error error = repo_load_file (self, file_name, &sound_font);
++  if (sound_font)
++    {
++      UndoDescriptor<SoundFontImpl> sound_font_descriptor = undo_descriptor 
(*sound_font->as<SoundFontImpl*>());
++      auto remove_sound_font_lambda = [sound_font_descriptor] (SoundFontRepoImpl &self, BseUndoStack 
*ustack) -> Error {
++        SoundFontImpl &sound_font = self.undo_resolve (sound_font_descriptor);
++        self.remove_sound_font (sound_font);
++        return Error::NONE;
++      };
++      push_undo (__func__, *this, remove_sound_font_lambda);
++    }
++  return error;
++}
++
++Error
++SoundFontRepoImpl::remove_sound_font (SoundFontIface &sound_font_iface)
++{
++  BseSoundFontRepo *self = as<BseSoundFontRepo*>();
++  BseSoundFont *sound_font = sound_font_iface.as<BseSoundFont*>();
++
++  assert_return (sound_font->parent == self, Bse::Error::INTERNAL);
++
++  if (BSE_SOURCE_PREPARED (self))
++    {
++      /* Don't allow unloading sound fonts which could be required by engine
++       * modules in the DSP thread currently producing audio output.
++       */
++      return Bse::Error::SOURCE_BUSY;
++    }
++  BseUndoStack *ustack = bse_item_undo_open (self, __func__);
++  bse_container_uncross_undoable (self, sound_font);    // removes object references
++  if (sound_font)                                       // push undo for 'remove_backedup'
++    {
++      UndoDescriptor<SoundFontImpl> sound_font_descriptor = undo_descriptor 
(*sound_font->as<SoundFontImpl*>());
++      auto remove_sound_font_lambda = [sound_font_descriptor] (SoundFontRepoImpl &self, BseUndoStack 
*ustack) -> Error {
++        SoundFontImpl &sound_font = self.undo_resolve (sound_font_descriptor);
++        self.remove_sound_font (sound_font);
++        return Error::NONE;
++      };
++      push_undo_to_redo (__func__, *this, remove_sound_font_lambda);
++    }
++  bse_container_remove_backedup (self, sound_font, ustack);   // removes without undo
++  bse_item_undo_close (ustack);
++
++  return Bse::Error::NONE;
++
++}
++
++} // Bse
diff --cc bse/bsesoundfontrepo.hh
index f76831b,0000000..2588758
mode 100644,000000..100644
--- a/bse/bsesoundfontrepo.hh
+++ b/bse/bsesoundfontrepo.hh
@@@ -1,68 -1,0 +1,82 @@@
 +// Licensed GNU LGPL v2.1 or later: http://www.gnu.org/licenses/lgpl.html
 +#ifndef       __BSE_SOUND_FONT_REPO_HH__
 +#define       __BSE_SOUND_FONT_REPO_HH__
 +
 +#include      <bse/bsesuper.hh>
 +#include        <fluidsynth.h>
 +#include        <bse/bsesoundfontosc.hh>
 +#include        <bse/bseengine.hh>
 +
 +G_BEGIN_DECLS
 +
 +#define BSE_TYPE_SOUND_FONT_REPO              (BSE_TYPE_ID (BseSoundFontRepo))
 +#define BSE_SOUND_FONT_REPO(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), 
BSE_TYPE_SOUND_FONT_REPO, BseSoundFontRepo))
 +#define BSE_SOUND_FONT_REPO_CLASS(class)      (G_TYPE_CHECK_CLASS_CAST ((class), BSE_TYPE_SOUND_FONT_REPO, 
BseSoundFontRepoClass))
 +#define BSE_IS_SOUND_FONT_REPO(object)                (G_TYPE_CHECK_INSTANCE_TYPE ((object), 
BSE_TYPE_SOUND_FONT_REPO))
 +#define BSE_IS_SOUND_FONT_REPO_CLASS(class)   (G_TYPE_CHECK_CLASS_TYPE ((class), BSE_TYPE_SOUND_FONT_REPO))
 +#define BSE_SOUND_FONT_REPO_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), 
BSE_TYPE_SOUND_FONT_REPO, BseSoundFontRepoClass))
 +
 +#define BSE_FLUID_SYNTH_PROGRAM_SELECT -1
 +
 +struct BseFluidEvent {
 +  guint64            tick_stamp;
 +  int                channel;
 +  int              command;
 +  int              arg1;
 +  int              arg2;
 +  int                sfont_id;          /* required for program selection only */
 +};
 +
 +struct BseSoundFontRepo : BseSuper {
 +  Bse::Mutex       fluid_synth_mutex;
 +  fluid_settings_t  *fluid_settings;
 +  fluid_synth_t     *fluid_synth;
 +  SfiRing           *fluid_events;
 +  guint              fluid_mix_freq;
 +
 +  guint              n_fluid_channels;
 +  float                  **channel_values_left;     /* [0..n_fluid_channels-1] */
 +  float                  **channel_values_right;    /* [0..n_fluid_channels-1] */
 +  guint64          channel_values_tick_stamp;
 +  gint              *n_silence_samples;       /* [0..n_fluid_channels-1] */
 +
 +  guint              n_oscs;
 +  BseSoundFontOsc  **oscs;                  /* [0..n_oscs-1] */
 +  guint                   *channel_map;             /* [0..n_oscs-1] */
 +
 +  int              n_channel_oscs_active;       /* SoundFontOscs with an active module in the engine thread 
*/
 +
 +  GList             *sound_fonts;
 +};
 +
 +struct BseSoundFontRepoClass : BseSuperClass
 +{};
 +
 +
 +/* --- prototypes --- */
 +void         bse_sound_font_repo_list_all_presets   (BseSoundFontRepo *sfrepo,
 +                                                     BseIt3mSeq       *items);
 +fluid_synth_t *bse_sound_font_repo_lock_fluid_synth   (BseSoundFontRepo *sfrepo);
 +void           bse_sound_font_repo_unlock_fluid_synth (BseSoundFontRepo *sfrepo);
 +int          bse_sound_font_repo_add_osc            (BseSoundFontRepo *sfrepo,
 +                                                     BseSoundFontOsc  *osc);
 +void           bse_sound_font_repo_remove_osc         (BseSoundFontRepo *sfrepo,
 +                                                     int               osc_id);
 +
 +G_END_DECLS
 +
++namespace Bse {
++
++class SoundFontRepoImpl : public SuperImpl, public virtual SoundFontRepoIface {
++protected:
++  virtual  ~SoundFontRepoImpl ();
++public:
++  explicit      SoundFontRepoImpl (BseObject*);
++  virtual Error load_file         (const String &file_name) override;
++  virtual Error remove_sound_font (SoundFontIface &wave) override;
++};
++
++} // Bse
++
++
 +#endif /* __BSE_SOUND_FONT_REPO_HH__ */
diff --cc po/POTSCAN
index 8665400,2db2d88..272ec9e
--- a/po/POTSCAN
+++ b/po/POTSCAN
@@@ -86,8 -85,6 +86,7 @@@ bse/bsepcmoutput.c
  bse/bseserver.cc
  bse/bsesnooper.cc
  bse/bsesong.cc
- bse/bsesong.proc
 +bse/bsesoundfontosc.cc
  bse/bsesource.proc
  bse/bsestandardosc.cc
  bse/bsesubiport.cc


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]