[beast/wip/timj/soundfont: 1/13] BSE: SF2: Merge and squash branch 'soundfont-support'
- From: Tim Janik <timj src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [beast/wip/timj/soundfont: 1/13] BSE: SF2: Merge and squash branch 'soundfont-support'
- Date: Mon, 19 Sep 2016 08:17:24 +0000 (UTC)
commit c0dd83c05aacb5c12e3210b438131f1a9e69e060
Author: Stefan Westerfeld <stefan space twc de>
Date: Wed Feb 27 01:38:58 2013 +0100
BSE: SF2: Merge and squash branch 'soundfont-support'
[Originally pulled as several changes from Stefan Westerfeld, squashed,
rebased, resolved conflicts and fixed messages. -- Tim Janik]
PO: add bstsoundfontview and bsesoundfontosc to translations
BST: remove sound font related debugging code
BUILD: include the right sound font header file into beast_headers
BST: Add necessary gui support for browsing sound fonts
BST: add new sound font view radget
BST: add sound font view as widget
BST: implement browsing and selection of available sound fonts
Selecting sound font presets in now supported as an alternative to synth
and wave selection.
BST: add code for sound font repository super class
BST: add file dialog support for loading sound fonts
BUILD: add beast-gtk files for sound font support
BSE: implement BseSoundFontOsc, which wraps fluid synth computations.
BSE: add sound font support for bse projects wrapping fluid synths structures.
BSE: add zintern snet for sound font preset replay
BSE: allow sound font presets as instruments for a track
BSE: implement new concept: BseStorageBlob
A blob is a binary large object, that is some binary data which is stored along
with the other data. It was designed to store sound font files within .bse
files, while still allowing fluid synth to access the file directly. Temp files
are used to give fluid synth direct access to a file.
BSE: add global sound font repository to each project
This repo is similar to the wave repo; it contains sound fonts used by a
project.
BSE: add code which allows modules to get midi events from receiver
This is implemented by registering a midi event handler function. Sound font
support via fluid synth needs this possibility.
BSE: add BseSoundFont* typedefs
BSE: remove unnecessary code in constant module
BUILD: add new files for sound font support
BUILD: add configure check for libfluidsynth, required for SoundFonts
beast-gtk/Makefile.am | 4 +-
beast-gtk/bstfiledialog.cc | 47 +++
beast-gtk/bstfiledialog.hh | 35 ++-
beast-gtk/bstsoundfontpresetview.c | 47 +++
beast-gtk/bstsoundfontpresetview.h | 52 +++
beast-gtk/bstsoundfontview.c | 155 +++++++++
beast-gtk/bstsoundfontview.h | 55 +++
beast-gtk/bstsupershell.cc | 17 +
beast-gtk/bsttracksynthdialog.cc | 31 ++-
beast-gtk/bsttracksynthdialog.hh | 9 +-
beast-gtk/bsttrackview.cc | 30 ++-
beast-gtk/bstutils.cc | 3 +
beast-gtk/dialogs/radgets-beast.xml | 19 +
bse/Makefile.am | 4 +
bse/bsedefs.hh | 6 +
bse/bsemidireceiver.cc | 272 ++++++++++++----
bse/bsemidireceiver.hh | 14 +
bse/bseproject.cc | 27 ++-
bse/bseproject.hh | 1 +
bse/bseproject.proc | 30 ++-
bse/bsesoundfont.c | 394 ++++++++++++++++++++++
bse/bsesoundfont.h | 59 ++++
bse/bsesoundfontosc.c | 617 +++++++++++++++++++++++++++++++++++
bse/bsesoundfontosc.h | 78 +++++
bse/bsesoundfontpreset.c | 195 +++++++++++
bse/bsesoundfontpreset.h | 54 +++
bse/bsesoundfontrepo.c | 393 ++++++++++++++++++++++
bse/bsesoundfontrepo.h | 94 ++++++
bse/bsesoundfontrepo.proc | 155 +++++++++
bse/bsestorage.cc | 372 ++++++++++++++++++++-
bse/bsestorage.hh | 16 +
bse/bsetrack.cc | 120 ++++++-
bse/bsetrack.hh | 5 +
bse/zintern/Makefile.am | 1 +
bse/zintern/sound-font-snet.bse | 17 +
configure.ac | 5 +
po/POTSCAN | 2 +
37 files changed, 3305 insertions(+), 130 deletions(-)
---
diff --git a/beast-gtk/Makefile.am b/beast-gtk/Makefile.am
index 60dcdbe..d57072f 100644
--- a/beast-gtk/Makefile.am
+++ b/beast-gtk/Makefile.am
@@ -36,7 +36,7 @@ beast_headers = $(strip \
bstusermessage.hh bstdial.hh bsttracksynthdialog.hh bstwaveeditor.hh \
bstzoomedwindow.hh bstskinconfig.hh bstmsgabsorb.hh bstsampleeditor.hh \
bstrackview.hh bsttreestores.hh bstbseutils.hh bstutils.hh \
- bstdefs.hh \
+ bstdefs.hh bstsoundfontview.h bstsoundfontpresetview.h \
)
EXTRA_DIST += $(beast_headers)
# BEAST sources to build the program from
@@ -58,7 +58,7 @@ beast_sources = $(strip \
bstusermessage.cc bstdial.cc bsttracksynthdialog.cc bstwaveeditor.cc \
bstzoomedwindow.cc bstskinconfig.cc bstmsgabsorb.cc bstsampleeditor.cc \
bstrackview.cc bsttreestores.cc bstbseutils.cc bstutils.cc \
- $(PROFILE_SOURCE) \
+ bstsoundfontview.c bstsoundfontpresetview.c $(PROFILE_SOURCE) \
)
# BEAST sources that get included (don't have own .lo rules)
beast_extra_files = $(strip \
diff --git a/beast-gtk/bstfiledialog.cc b/beast-gtk/bstfiledialog.cc
index 231a4a9..0e0b6ee 100644
--- a/beast-gtk/bstfiledialog.cc
+++ b/beast-gtk/bstfiledialog.cc
@@ -228,6 +228,16 @@ bst_file_dialog_global_wave (void)
}
static BstFileDialog*
+bst_file_dialog_global_sound_font (void)
+{
+ static BstFileDialog *singleton = NULL;
+ if (!singleton)
+ singleton = g_object_new (BST_TYPE_FILE_DIALOG, NULL);
+ return singleton;
+}
+
+
+static BstFileDialog*
bst_file_dialog_global_effect (void)
{
static BstFileDialog *singleton = NULL;
@@ -317,6 +327,7 @@ bst_file_dialog_set_mode (BstFileDialog *self,
/* handle tree visibility */
switch (mode & BST_FILE_DIALOG_MODE_MASK)
{
+ case BST_FILE_DIALOG_LOAD_SOUND_FONT:
case BST_FILE_DIALOG_LOAD_WAVE:
g_free (self->search_path);
self->search_path = g_strdup (bse_server_get_sample_path (BSE_SERVER));
@@ -325,6 +336,7 @@ bst_file_dialog_set_mode (BstFileDialog *self,
gxk_notebook_set_current_page_widget (GTK_NOTEBOOK (self->notebook), self->fpage);
g_object_set (self->notebook, "show_border", TRUE, "show_tabs", TRUE, NULL);
break;
+ case BST_FILE_DIALOG_LOAD_SOUND_FONT_LIB:
case BST_FILE_DIALOG_LOAD_WAVE_LIB:
g_free (self->search_path);
self->search_path = g_strdup (bse_server_get_sample_path (BSE_SERVER));
@@ -768,6 +780,37 @@ bst_file_dialog_load_wave (BstFileDialog *self,
}
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"), sound_font_repo);
+ gxk_widget_showraise (widget);
+
+ return widget;
+}
+
+static gboolean
+bst_file_dialog_load_sound_font (BstFileDialog *self,
+ const gchar *file_name)
+{
+ BseErrorType error;
+
+ gxk_status_printf (0, NULL, _("Loading sound font `%s'"), file_name);
+ error = bse_sound_font_repo_load_file (self->proxy, file_name);
+ bst_status_eprintf (error, _("Loading sound font `%s'"), file_name);
+ if (error)
+ 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);
@@ -905,6 +948,10 @@ bst_file_dialog_activate (BstFileDialog *self)
case BST_FILE_DIALOG_LOAD_WAVE_LIB:
popdown = bst_file_dialog_load_wave (self, file_name);
break;
+ case BST_FILE_DIALOG_LOAD_SOUND_FONT:
+ case BST_FILE_DIALOG_LOAD_SOUND_FONT_LIB:
+ popdown = bst_file_dialog_load_sound_font (self, file_name);
+ break;
default: ;
}
if (swin)
diff --git a/beast-gtk/bstfiledialog.hh b/beast-gtk/bstfiledialog.hh
index 93fc348..9290588 100644
--- a/beast-gtk/bstfiledialog.hh
+++ b/beast-gtk/bstfiledialog.hh
@@ -24,21 +24,23 @@ typedef struct _BstFileDialogClass BstFileDialogClass;
/* --- structures --- */
typedef enum {
- BST_FILE_DIALOG_OPEN_PROJECT = 0x0001,
- BST_FILE_DIALOG_MERGE_PROJECT = 0x0002,
- BST_FILE_DIALOG_SAVE_PROJECT = 0x0003,
- BST_FILE_DIALOG_IMPORT_MIDI = 0x0004,
- BST_FILE_DIALOG_SELECT_FILE = 0x0008,
- BST_FILE_DIALOG_SELECT_DIR = 0x0009,
- BST_FILE_DIALOG_LOAD_WAVE = 0x0011,
- BST_FILE_DIALOG_LOAD_WAVE_LIB = 0x0012,
- BST_FILE_DIALOG_MERGE_EFFECT = 0x0021,
- BST_FILE_DIALOG_MERGE_INSTRUMENT = 0x0022,
- BST_FILE_DIALOG_SAVE_EFFECT = 0x0023,
- BST_FILE_DIALOG_SAVE_INSTRUMENT = 0x0024,
- BST_FILE_DIALOG_MODE_MASK = 0x00ff,
- BST_FILE_DIALOG_ALLOW_DIRS = 0x1000,
- BST_FILE_DIALOG_FLAG_MASK = 0xff00
+ BST_FILE_DIALOG_OPEN_PROJECT = 0x0001,
+ BST_FILE_DIALOG_MERGE_PROJECT = 0x0002,
+ BST_FILE_DIALOG_SAVE_PROJECT = 0x0003,
+ BST_FILE_DIALOG_IMPORT_MIDI = 0x0004,
+ BST_FILE_DIALOG_SELECT_FILE = 0x0008,
+ BST_FILE_DIALOG_SELECT_DIR = 0x0009,
+ BST_FILE_DIALOG_LOAD_WAVE = 0x0011,
+ BST_FILE_DIALOG_LOAD_WAVE_LIB = 0x0012,
+ BST_FILE_DIALOG_LOAD_SOUND_FONT = 0x0013,
+ BST_FILE_DIALOG_LOAD_SOUND_FONT_LIB = 0x0014,
+ BST_FILE_DIALOG_MERGE_EFFECT = 0x0021,
+ BST_FILE_DIALOG_MERGE_INSTRUMENT = 0x0022,
+ BST_FILE_DIALOG_SAVE_EFFECT = 0x0023,
+ BST_FILE_DIALOG_SAVE_INSTRUMENT = 0x0024,
+ BST_FILE_DIALOG_MODE_MASK = 0x00ff,
+ BST_FILE_DIALOG_ALLOW_DIRS = 0x1000,
+ BST_FILE_DIALOG_FLAG_MASK = 0xff00
} BstFileDialogMode;
struct _BstFileDialog
{
@@ -94,6 +96,9 @@ GtkWidget* bst_file_dialog_popup_select_dir (gpointer parent_widget);
GtkWidget* bst_file_dialog_popup_load_wave (gpointer parent_widget,
SfiProxy wave_repo,
gboolean show_lib);
+GtkWidget* bst_file_dialog_popup_load_sound_font (gpointer parent_widget,
+ SfiProxy sound_font_repo,
+ gboolean show_lib);
void bst_file_dialog_set_mode (BstFileDialog *self,
gpointer parent_widget,
BstFileDialogMode mode,
diff --git a/beast-gtk/bstsoundfontpresetview.c b/beast-gtk/bstsoundfontpresetview.c
new file mode 100644
index 0000000..60ac02e
--- /dev/null
+++ b/beast-gtk/bstsoundfontpresetview.c
@@ -0,0 +1,47 @@
+/* BEAST - Bedevilled Audio System
+ * Copyright (C) 1998-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bstsoundfontpresetview.h"
+
+/* --- functions --- */
+
+G_DEFINE_TYPE (BstSoundFontPresetView, bst_sound_font_preset_view, BST_TYPE_ITEM_VIEW);
+
+
+static void
+bst_sound_font_preset_view_class_init (BstSoundFontPresetViewClass *class)
+{
+ BstItemViewClass *item_view_class = BST_ITEM_VIEW_CLASS (class);
+
+ item_view_class->item_type = "BseSoundFontPreset";
+}
+
+static void
+bst_sound_font_preset_view_init (BstSoundFontPresetView *self)
+{
+ BstItemView *iview = BST_ITEM_VIEW (self);
+}
+
+GtkWidget*
+bst_sound_font_preset_view_new()
+{
+ GtkWidget *sound_font_preset_view;
+
+ sound_font_preset_view = gtk_widget_new (BST_TYPE_SOUND_FONT_PRESET_VIEW, NULL);
+
+ return sound_font_preset_view;
+}
diff --git a/beast-gtk/bstsoundfontpresetview.h b/beast-gtk/bstsoundfontpresetview.h
new file mode 100644
index 0000000..0afb027
--- /dev/null
+++ b/beast-gtk/bstsoundfontpresetview.h
@@ -0,0 +1,52 @@
+/* BEAST - Bedevilled Audio System
+ * Copyright (C) 1998-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+
+#ifndef __BST_SOUND_FONT_PRESET_VIEW_H__
+#define __BST_SOUND_FONT_PRESET_VIEW_H__
+
+#include "bstitemview.h"
+
+G_BEGIN_DECLS
+
+/* --- Gtk+ type macros --- */
+#define BST_TYPE_SOUND_FONT_PRESET_VIEW (bst_sound_font_preset_view_get_type ())
+#define BST_SOUND_FONT_PRESET_VIEW(object) (GTK_CHECK_CAST ((object),
BST_TYPE_SOUND_FONT_PRESET_VIEW, BstSoundFontPresetView))
+#define BST_SOUND_FONT_PRESET_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass),
BST_TYPE_SOUND_FONT_PRESET_VIEW, BstSoundFontPresetViewClass))
+#define BST_IS_SOUND_FONT_PRESET_VIEW(object) (GTK_CHECK_TYPE ((object),
BST_TYPE_SOUND_FONT_PRESET_VIEW))
+#define BST_IS_SOUND_FONT_PRESET_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass),
BST_TYPE_SOUND_FONT_PRESET_VIEW))
+#define BST_SOUND_FONT_PRESET_VIEW_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj),
BST_TYPE_SOUND_FONT_PRESET_VIEW, BstSoundFontPresetViewClass))
+
+
+typedef struct _BstSoundFontPresetView BstSoundFontPresetView;
+typedef struct _BstSoundFontPresetViewClass BstSoundFontPresetViewClass;
+struct _BstSoundFontPresetView
+{
+ BstItemView parent_object;
+};
+
+struct _BstSoundFontPresetViewClass
+{
+ BstItemViewClass parent_class;
+};
+
+/* --- prototypes --- */
+
+GType bst_sound_font_preset_view_get_type (void);
+GtkWidget* bst_sound_font_preset_view_new (void);
+
+#endif /* __BST_SOUND_FONT_PRESET_VIEW_H__ */
diff --git a/beast-gtk/bstsoundfontview.c b/beast-gtk/bstsoundfontview.c
new file mode 100644
index 0000000..f2929d9
--- /dev/null
+++ b/beast-gtk/bstsoundfontview.c
@@ -0,0 +1,155 @@
+/* BEAST - Bedevilled Audio System
+ * Copyright (C) 1998-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bstsoundfontview.h"
+#include "bstsoundfontpresetview.h"
+#include "bstfiledialog.h"
+
+/* --- 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 *class)
+{
+ BstItemViewClass *item_view_class = BST_ITEM_VIEW_CLASS (class);
+
+ 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 = 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 = 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);
+ 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 --git a/beast-gtk/bstsoundfontview.h b/beast-gtk/bstsoundfontview.h
new file mode 100644
index 0000000..bf0e27f
--- /dev/null
+++ b/beast-gtk/bstsoundfontview.h
@@ -0,0 +1,55 @@
+/* BEAST - Bedevilled Audio System
+ * Copyright (C) 1998-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+
+#ifndef __BST_SOUND_FONT_VIEW_H__
+#define __BST_SOUND_FONT_VIEW_H__
+
+#include "bstitemview.h"
+#include "bstsoundfontpresetview.h"
+
+G_BEGIN_DECLS
+
+/* --- Gtk+ type macros --- */
+#define BST_TYPE_SOUND_FONT_VIEW (bst_sound_font_view_get_type ())
+#define BST_SOUND_FONT_VIEW(object) (GTK_CHECK_CAST ((object), BST_TYPE_SOUND_FONT_VIEW,
BstSoundFontView))
+#define BST_SOUND_FONT_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass),
BST_TYPE_SOUND_FONT_VIEW, BstSoundFontViewClass))
+#define BST_IS_SOUND_FONT_VIEW(object) (GTK_CHECK_TYPE ((object), BST_TYPE_SOUND_FONT_VIEW))
+#define BST_IS_SOUND_FONT_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass),
BST_TYPE_SOUND_FONT_VIEW))
+#define BST_SOUND_FONT_VIEW_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), BST_TYPE_SOUND_FONT_VIEW,
BstSoundFontViewClass))
+
+
+typedef struct _BstSoundFontView BstSoundFontView;
+typedef struct _BstSoundFontViewClass BstSoundFontViewClass;
+struct _BstSoundFontView
+{
+ BstItemView parent_object;
+ BstSoundFontPresetView *preset_view;
+};
+
+struct _BstSoundFontViewClass
+{
+ BstItemViewClass parent_class;
+};
+
+/* --- prototypes --- */
+
+GType bst_sound_font_view_get_type (void);
+GtkWidget* bst_sound_font_view_new (SfiProxy sfont_repo);
+SfiProxy bst_sound_font_view_get_preset (BstSoundFontView *self);
+
+#endif /* __BST_SOUND_FONT_VIEW_H__ */
diff --git a/beast-gtk/bstsupershell.cc b/beast-gtk/bstsupershell.cc
index 5f06742..224c4df 100644
--- a/beast-gtk/bstsupershell.cc
+++ b/beast-gtk/bstsupershell.cc
@@ -7,6 +7,7 @@
#include "bstbusview.hh"
#include "bstwaveview.hh"
#include "bstrackview.hh"
+#include "bstsoundfontview.h"
#include "bstsnetrouter.hh"
#include "bstgconfig.hh"
#include <string.h>
@@ -220,6 +221,20 @@ super_shell_build_wave_repo (BstSuperShell *self,
gxk_notebook_create_tabulator (_("Properties"), BST_STOCK_PROPERTIES, NULL));
}
+static void
+super_shell_build_sound_font_repo (BstSuperShell *self,
+ GtkNotebook *notebook)
+{
+ SfiProxy sfrepo = self->super;
+
+ gtk_notebook_append_page (notebook,
+ bst_sound_font_view_new (sfrepo),
+ gxk_notebook_create_tabulator (_("Sound Fonts"), BST_STOCK_MINI_WAVE_REPO,
NULL));
+ gtk_notebook_append_page (notebook,
+ bst_param_view_new (sfrepo),
+ gxk_notebook_create_tabulator (_("Properties"), BST_STOCK_PROPERTIES, NULL));
+}
+
static GtkNotebook*
create_notebook (BstSuperShell *self)
{
@@ -244,6 +259,8 @@ super_shell_add_views (BstSuperShell *self)
super_shell_build_song (self, create_notebook (self));
else if (BSE_IS_WAVE_REPO (self->super))
super_shell_build_wave_repo (self, create_notebook (self));
+ else if (BSE_IS_SOUND_FONT_REPO (self->super))
+ super_shell_build_sound_font_repo (self, create_notebook (self));
else /* BSE_IS_SNET (self->super) */
super_shell_build_snet (self, create_notebook (self));
}
diff --git a/beast-gtk/bsttracksynthdialog.cc b/beast-gtk/bsttracksynthdialog.cc
index dcad9d9..16ec978 100644
--- a/beast-gtk/bsttracksynthdialog.cc
+++ b/beast-gtk/bsttracksynthdialog.cc
@@ -42,7 +42,7 @@ bst_track_synth_dialog_init (BstTrackSynthDialog *self)
GXK_DIALOG_POPUP_POS |
GXK_DIALOG_MODAL),
NULL);
- gxk_dialog_set_sizes (GXK_DIALOG (self), 550, 300, 600, 320);
+ gxk_dialog_set_sizes (GXK_DIALOG (self), 550, 300, 600, 450);
/* notebook */
self->notebook = (GtkNotebook*) g_object_new (GXK_TYPE_NOTEBOOK,
@@ -105,6 +105,9 @@ bst_track_synth_dialog_init (BstTrackSynthDialog *self)
self->wpage = (GtkWidget*) g_object_new (BST_TYPE_WAVE_VIEW, "visible", TRUE, NULL);
gxk_notebook_append (self->notebook, self->wpage, "wave", TRUE);
bst_wave_view_set_editable (BST_WAVE_VIEW (self->wpage), FALSE);
+ /* sound font view */
+ self->sfont_page = g_object_new (BST_TYPE_SOUND_FONT_VIEW, "visible", TRUE, NULL);
+ gxk_notebook_append (self->notebook, self->sfont_page, "sound_font", TRUE);
/* provide buttons */
self->ok = gxk_dialog_default_action_swapped (GXK_DIALOG (self), BST_STOCK_OK, (void*)
bst_track_synth_dialog_activate, self);
@@ -221,6 +224,9 @@ bst_track_synth_dialog_popup (gpointer parent_widget,
const gchar *wrepo_label,
const gchar *wrepo_tooltip,
SfiProxy wrepo,
+ const gchar *sfrepo_label,
+ const gchar *sfrepo_tooltip,
+ SfiProxy sfrepo,
BstTrackSynthDialogSelected selected_callback,
gpointer selected_data,
GxkFreeFunc selected_cleanup)
@@ -231,6 +237,8 @@ bst_track_synth_dialog_popup (gpointer parent_widget,
candidate_label = "";
if (!wrepo_label)
wrepo_label = "";
+ if (!sfrepo_label)
+ sfrepo_label = "";
bst_track_synth_dialog_setup (self, NULL, NULL, 0);
@@ -238,8 +246,10 @@ bst_track_synth_dialog_popup (gpointer parent_widget,
gxk_widget_set_tooltip (self->tview, candidate_tooltip);
g_object_set (gtk_notebook_get_tab_label (self->notebook, self->wpage), "label", wrepo_label, NULL);
gxk_widget_set_tooltip (BST_ITEM_VIEW (self->wpage)->tree, wrepo_tooltip);
+ g_object_set (gtk_notebook_get_tab_label (self->notebook, self->sfont_page), "label", sfrepo_label, NULL);
+ gxk_widget_set_tooltip (BST_ITEM_VIEW (self->sfont_page)->tree, sfrepo_tooltip);
- bst_track_synth_dialog_set (self, candidates, wrepo);
+ bst_track_synth_dialog_set (self, candidates, wrepo, sfrepo);
bst_track_synth_dialog_setup (self, parent_widget,
/* TRANSLATORS: this is a dialog title and %s is replaced by an object name
*/
_("Synthesizer Selection: %s"),
@@ -256,14 +266,17 @@ bst_track_synth_dialog_popup (gpointer parent_widget,
void
bst_track_synth_dialog_set (BstTrackSynthDialog *self,
BseItemSeq *iseq,
- SfiProxy wrepo)
+ SfiProxy wrepo,
+ SfiProxy sfrepo)
{
g_return_if_fail (BST_IS_TRACK_SYNTH_DIALOG (self));
bst_item_view_set_container (BST_ITEM_VIEW (self->wpage), wrepo);
+ bst_item_view_set_container (BST_ITEM_VIEW (self->sfont_page), sfrepo);
bst_item_seq_store_set (self->pstore, iseq);
- g_object_set (self->wpage, "visible", wrepo != 0, NULL);
g_object_set (self->spage, "visible", iseq != NULL, NULL);
+ g_object_set (self->wpage, "visible", wrepo != 0, NULL);
+ g_object_set (self->sfont_page, "visible", sfrepo != 0, NULL);
}
static void
@@ -288,8 +301,14 @@ bst_track_synth_dialog_activate (BstTrackSynthDialog *self)
proxy = bst_item_seq_store_get_from_iter (self->pstore, &piter);
}
}
- else if (self->wpage)
- proxy = bst_item_view_get_current (BST_ITEM_VIEW (self->wpage));
+ else if (self->wpage && gxk_widget_viewable (GTK_WIDGET (self->wpage)))
+ {
+ proxy = bst_item_view_get_current (BST_ITEM_VIEW (self->wpage));
+ }
+ else if (self->sfont_page && gxk_widget_viewable (GTK_WIDGET (self->sfont_page)))
+ {
+ proxy = bst_sound_font_view_get_preset (BST_SOUND_FONT_VIEW (self->sfont_page));
+ }
/* ignore_activate guards against multiple clicks */
self->ignore_activate = TRUE;
diff --git a/beast-gtk/bsttracksynthdialog.hh b/beast-gtk/bsttracksynthdialog.hh
index df50a4f..90cbe68 100644
--- a/beast-gtk/bsttracksynthdialog.hh
+++ b/beast-gtk/bsttracksynthdialog.hh
@@ -4,6 +4,7 @@
#include "bstutils.hh"
#include "bstwaveview.hh"
+#include "bstsoundfontview.h"
G_BEGIN_DECLS
@@ -29,6 +30,7 @@ struct _BstTrackSynthDialog
GtkNotebook *notebook;
GtkWidget *wpage; /* wave repo item view */
GtkWidget *spage; /* synth list */
+ GtkWidget *sfont_page; /* sound font patch selection */
GtkWidget *ok; /* ok button */
GtkWindow *parent_window;
guint ignore_activate : 1;
@@ -54,13 +56,16 @@ GtkWidget* bst_track_synth_dialog_popup (gpointer parent_
const gchar *wrepo_label,
const gchar *wrepo_tooltip,
SfiProxy wrepo,
+ const gchar *sfrepo_label,
+ const gchar *sfrepo_tooltip,
+ SfiProxy sfrepo,
BstTrackSynthDialogSelected selected_callback,
gpointer selected_data,
GxkFreeFunc selected_cleanup);
void bst_track_synth_dialog_set (BstTrackSynthDialog *self,
BseItemSeq *iseq,
- SfiProxy wrepo);
-
+ SfiProxy wrepo,
+ SfiProxy sfrepo);
G_END_DECLS
diff --git a/beast-gtk/bsttrackview.cc b/beast-gtk/bsttrackview.cc
index 60bcc5a..fa94ea0 100644
--- a/beast-gtk/bsttrackview.cc
+++ b/beast-gtk/bsttrackview.cc
@@ -120,7 +120,7 @@ track_view_fill_value (BstItemView *iview,
const gchar *string;
gboolean vbool;
SfiInt vint;
- SfiProxy snet, wave;
+ SfiProxy snet, wave, sound_font_preset;
BseItemSeq *iseq;
SfiSeq *seq;
case COL_SEQID:
@@ -139,8 +139,18 @@ track_view_fill_value (BstItemView *iview,
break;
case COL_SYNTH:
snet = 0;
- bse_proxy_get (item, "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);
+ 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);
@@ -184,11 +194,13 @@ track_view_synth_edited (BstTrackView *self,
{
SfiProxy proxy = 0;
GSList *slist = NULL;
- /* list possible snet/wave candidates */
+ /* list possible snet/wave/sound_font_preset candidates */
BsePropertyCandidates *pc = bse_item_get_property_candidates (item, "snet");
slist = g_slist_append (slist, pc->items);
pc = bse_item_get_property_candidates (item, "wave");
slist = g_slist_append (slist, pc->items);
+ pc = bse_item_get_property_candidates (item, "sound_font_preset");
+ slist = g_slist_append (slist, pc->items);
/* find best match */
proxy = bst_item_seq_list_match (slist, text);
g_slist_free (slist);
@@ -196,11 +208,13 @@ track_view_synth_edited (BstTrackView *self,
bse_proxy_set (item, "snet", proxy, NULL);
else if (proxy && BSE_IS_WAVE (proxy))
bse_proxy_set (item, "wave", proxy, NULL);
+ else if (proxy && BSE_IS_SOUND_FONT_PRESET (proxy))
+ bse_proxy_set (item, "sound_font_preset", proxy, NULL);
else
- bse_proxy_set (item, "snet", 0, "wave", 0, NULL);
+ bse_proxy_set (item, "snet", 0, "wave", 0, "sound_font_preset", 0, NULL);
}
else
- bse_proxy_set (item, "snet", 0, "wave", 0, NULL);
+ bse_proxy_set (item, "snet", 0, "wave", 0, "sound_font_preset", 0, NULL);
}
}
@@ -281,6 +295,9 @@ track_view_synth_popup (BstTrackView *self,
_("Available Waves"),
_("List of available waves to choose a track
instrument from"),
bse_project_get_wave_repo (bse_item_get_project
(item)),
+ _("Available Sound Fonts"),
+ _("List of available sound fonts to choose track
instrument from"),
+ bse_project_get_sound_font_repo
(bse_item_get_project (item)),
track_view_synth_popup_cb, g_memdup (&sdata,
sizeof (sdata)), track_view_synth_popup_cleanup);
gxk_cell_renderer_popup_dialog (pcell, dialog);
}
@@ -308,6 +325,7 @@ track_view_post_synth_popup (BstTrackView *self,
GtkWidget *dialog = bst_track_synth_dialog_popup (self, item,
pc->label, pc->tooltip, pc->items,
NULL, NULL, 0,
+ NULL, NULL, 0,
track_view_synth_popup_cb, g_memdup (&sdata,
sizeof (sdata)), track_view_synth_popup_cleanup);
gxk_cell_renderer_popup_dialog (pcell, dialog);
}
diff --git a/beast-gtk/bstutils.cc b/beast-gtk/bstutils.cc
index af7ad9d..90a1afa 100644
--- a/beast-gtk/bstutils.cc
+++ b/beast-gtk/bstutils.cc
@@ -5,6 +5,7 @@
#include "bstmenus.hh"
#include "bsttrackview.hh"
#include "bstwaveview.hh"
+#include "bstsoundfontview.h"
#include "bstpartview.hh"
#include "bstbusmixer.hh"
#include "bstbuseditor.hh"
@@ -15,6 +16,7 @@
#include "bstgrowbar.hh"
#include "bstdbmeter.hh"
#include "bstscrollgraph.hh"
+
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
@@ -95,6 +97,7 @@ _bst_init_radgets (void)
gxk_radget_define_widget_type (BST_TYPE_HGROW_BAR);
gxk_radget_define_widget_type (BST_TYPE_VGROW_BAR);
gxk_radget_define_widget_type (BST_TYPE_WAVE_VIEW);
+ gxk_radget_define_widget_type (BST_TYPE_SOUND_FONT_VIEW);
gxk_radget_define_widget_type (BST_TYPE_PART_VIEW);
gxk_radget_define_widget_type (BST_TYPE_BUS_EDITOR);
gxk_radget_define_widget_type (BST_TYPE_BUS_MIXER);
diff --git a/beast-gtk/dialogs/radgets-beast.xml b/beast-gtk/dialogs/radgets-beast.xml
index 09e6e3f..5bb5118 100644
--- a/beast-gtk/dialogs/radgets-beast.xml
+++ b/beast-gtk/dialogs/radgets-beast.xml
@@ -209,6 +209,25 @@
</alignment>
</hbox>
</xdef:wave-view>
+
+ <!-- sound font view -->
+ <xdef:sound-font-view inherit="BstSoundFontView" border-width="3" >
+ <vbox spacing="3" >
+ <hbox spacing="3" >
+ <scrolled-window id="tree-area" pack:expand="1" >
+ <tree-view id="tree-view" height="120" width="200" />
+ </scrolled-window>
+ <alignment yscale="0" yalign="0" >
+ <vbox homogeneous="1" spacing="3" >
+ <big-button-factory actions="sound-font-view-actions" />
+ </vbox>
+ </alignment>
+ </hbox>
+ <scrolled-window id="preset-area" pack:expand="1" >
+ <tree-view id="preset-view" height="120" width="200" />
+ </scrolled-window>
+ </vbox>
+ </xdef:sound-font-view>
<!-- part view -->
<xdef:part-view inherit="BstPartView" border-width="3" >
diff --git a/bse/Makefile.am b/bse/Makefile.am
index cda233c..0d16dbb 100644
--- a/bse/Makefile.am
+++ b/bse/Makefile.am
@@ -46,6 +46,7 @@ bse_public_headers = $(strip \
bsesnet.hh bsesnooper.hh bsesong.hh bsesequencer.hh \
bsesource.hh bsestandardosc.hh bsestandardsynths.hh bsestorage.hh \
bseinstrumentoutput.hh bsesubiport.hh bseinstrumentinput.hh bsesuboport.hh \
+ bsesoundfont.h bsesoundfontpreset.h bsesoundfontosc.h \
bsesubsynth.hh bsesuper.hh bsetrack.hh bsetype.hh \
bseutils.hh bsemidivoice.hh bsewave.hh bsewaveosc.hh \
bsecsynth.hh bsewaverepo.hh bseladspamodule.hh bsepcmwriter.hh \
@@ -78,6 +79,8 @@ bse_sources = $(strip \
bsepcmdevice.cc bsepcmdevice-oss.cc bsepcmdevice-null.cc bseplugin.cc \
bseprocedure.cc bseproject.cc bsescripthelper.cc bseserver.cc \
bsesnet.cc bsesnooper.cc bsesong.cc bsesequencer.cc \
+ bsesoundfont.c bsesoundfontrepo.c bsesoundfontpreset.c bsesoundfontosc.c \
+ bsesoundfontrepo.c bsesoundfont.c bsesoundfontpreset.c bsesoundfontosc.c \
bsesource.cc bsestandardosc.cc bsestandardsynths.cc bsestorage.cc \
bseinstrumentoutput.cc bsesubiport.cc bseinstrumentinput.cc bsesuboport.cc \
bsesubsynth.cc bsesuper.cc bsetrack.cc bsetype.cc \
@@ -117,6 +120,7 @@ bse_proc_sources = $(strip \
bsejanitor.proc bsepart.proc bseparasite.proc bseprocedure.proc
bseproject.proc bsescripthelper.proc \
bseserver.proc bsesong.proc bsebus.proc bsesource.proc
bsecsynth.proc bsesnet.proc \
bsetrack.proc bseitem.proc bsewave.proc bsewaveosc.proc
bsewaverepo.proc \
+ bsesoundfontrepo.proc \
)
bse_proc_gen_sources = $(bse_proc_sources:.proc=.genprc.cc)
# non-compile and non-install sources required
diff --git a/bse/bsedefs.hh b/bse/bsedefs.hh
index 634e5df..f1f962e 100644
--- a/bse/bsedefs.hh
+++ b/bse/bsedefs.hh
@@ -65,6 +65,12 @@ struct BseSNetClass;
struct BseSong;
struct BseSongClass;
typedef struct _BseSongSequencer BseSongSequencer;
+typedef struct _BseSoundFont BseSoundFont;
+typedef struct _BseSoundFontClass BseSoundFontClass;
+typedef struct _BseSoundFontPreset BseSoundFontPreset;
+typedef struct _BseSoundFontPresetClass BseSoundFontPresetClass;
+typedef struct _BseSoundFontRepo BseSoundFontRepo;
+typedef struct _BseSoundFontRepoClass BseSoundFontRepoClass;
struct BseSource;
struct BseSourceClass;
struct BseStorage;
diff --git a/bse/bsemidireceiver.cc b/bse/bsemidireceiver.cc
index b72aa52..5ce7e47 100644
--- a/bse/bsemidireceiver.cc
+++ b/bse/bsemidireceiver.cc
@@ -11,6 +11,8 @@
#include <sfi/gbsearcharray.hh>
#include <map>
#include <set>
+#include <list>
+
namespace {
using namespace Bse;
using namespace std;
@@ -211,6 +213,31 @@ struct ControlValue {
}
};
+struct EventHandler
+{
+ guint midi_channel;
+ BseMidiEventHandler handler_func;
+ gpointer handler_data;
+ BseModule *module;
+
+ EventHandler (guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module) :
+ midi_channel (midi_channel),
+ handler_func (handler_func),
+ handler_data (handler_data),
+ module (module)
+ {
+ }
+ bool operator == (const EventHandler& other)
+ {
+ return (midi_channel == other.midi_channel &&
+ handler_func == other.handler_func &&
+ handler_data == other.handler_data &&
+ module == other.module);
+ }
+};
/* --- voice prototypes --- */
typedef struct VoiceSwitch VoiceSwitch;
@@ -226,6 +253,7 @@ struct MidiChannel {
guint n_voices;
VoiceSwitch **voices;
VoiceInputTable voice_input_table;
+ std::list<EventHandler> event_handlers;
MidiChannel (guint mc) :
midi_channel (mc),
poly_enabled (0)
@@ -245,6 +273,21 @@ struct MidiChannel {
if (poly_enabled)
poly_enabled--;
}
+ void
+ add_event_handler (const EventHandler& handler)
+ {
+ event_handlers.push_back (handler);
+ }
+ void
+ remove_event_handler (const EventHandler& handler)
+ {
+ list<EventHandler>::iterator hi = find (event_handlers.begin(), event_handlers.end(), handler);
+ g_return_if_fail (hi != event_handlers.end());
+ event_handlers.erase (hi);
+ }
+ bool
+ call_event_handlers (BseMidiEvent *event,
+ BseTrans *trans);
~MidiChannel()
{
if (vinput)
@@ -424,6 +467,24 @@ public:
ControlValue *cv = get_control_value (midi_channel, signal_type);
cv->remove_handler (handler_func, handler_data, module);
}
+ void
+ add_event_handler (guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module)
+ {
+ MidiChannel *channel = get_channel (midi_channel);
+ channel->add_event_handler (EventHandler (midi_channel, handler_func, handler_data, module));
+ }
+ void
+ remove_event_handler (guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module)
+ {
+ MidiChannel *channel = get_channel (midi_channel);
+ channel->remove_event_handler (EventHandler (midi_channel, handler_func, handler_data, module));
+ }
};
@@ -1019,6 +1080,35 @@ MidiChannel::no_poly_voice (const gchar *event_name,
mchannel->midi_channel, event_name, freq);
}
+bool
+MidiChannel::call_event_handlers (BseMidiEvent *event,
+ BseTrans *trans)
+{
+ bool success = false;
+ list<EventHandler>::iterator hi;
+ for (hi = event_handlers.begin(); hi != event_handlers.end(); hi++)
+ {
+ int activated = 0;
+ for (guint i = 0; i < n_voices; i++)
+ {
+ if (voices[i] && voices[i]->n_vinputs)
+ {
+ if (check_voice_switch_available_L (voices[i]))
+ {
+ activated++;
+ VoiceSwitch *vswitch = voices[i];
+ activate_voice_switch_L (vswitch, event->delta_time, trans);
+ }
+ }
+ }
+ if (!(activated <= 1))
+ g_warning (G_STRLOC ": midi event handling: assertion (activated <= 1) failed, activated = %d",
activated);
+ hi->handler_func (hi->handler_data, hi->module, event, trans);
+ success = true;
+ }
+ return success;
+}
+
void
MidiChannel::start_note (guint64 tick_stamp,
gfloat freq,
@@ -1488,6 +1578,40 @@ bse_midi_receiver_remove_control_handler (BseMidiReceiver *self,
}
void
+bse_midi_receiver_add_event_handler (BseMidiReceiver *self,
+ guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (midi_channel > 0);
+ g_return_if_fail (handler_func != NULL);
+ g_return_if_fail (module != NULL);
+
+ BSE_MIDI_RECEIVER_LOCK ();
+ self->add_event_handler (midi_channel, handler_func, handler_data, module);
+ BSE_MIDI_RECEIVER_UNLOCK ();
+}
+
+void
+bse_midi_receiver_remove_event_handler (BseMidiReceiver *self,
+ guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (midi_channel > 0);
+ g_return_if_fail (handler_func != NULL);
+ g_return_if_fail (module != NULL);
+
+ BSE_MIDI_RECEIVER_LOCK ();
+ self->remove_event_handler (midi_channel, handler_func, handler_data, module);
+ BSE_MIDI_RECEIVER_UNLOCK ();
+}
+
+void
bse_midi_receiver_channel_enable_poly (BseMidiReceiver *self,
guint midi_channel)
{
@@ -1938,79 +2062,85 @@ midi_receiver_process_event_L (BseMidiReceiver *self,
if (event->delta_time <= max_tick_stamp)
{
BseTrans *trans = bse_trans_open ();
+ MidiChannel *mchannel = self->peek_channel (event->channel);
self->events = sfi_ring_remove_node (self->events, self->events);
- switch (event->status)
+
+ bool event_handled = false;
+ if (mchannel)
+ event_handled = mchannel->call_event_handlers (event, trans);
+
+ if (!event_handled)
{
- MidiChannel *mchannel;
- case BSE_MIDI_NOTE_ON:
- mchannel = self->peek_channel (event->channel);
- EDEBUG ("MidiChannel[%u]: NoteOn %fHz Velo=%f (stamp:%llu)", event->channel,
- event->data.note.frequency, event->data.note.velocity, event->delta_time);
- if (mchannel)
- mchannel->start_note (event->delta_time,
- event->data.note.frequency,
- event->data.note.velocity,
- trans);
- else
- sfi_diag ("ignoring note-on (%fHz) for foreign midi channel: %u", event->data.note.frequency,
event->channel);
- break;
- case BSE_MIDI_KEY_PRESSURE:
- case BSE_MIDI_NOTE_OFF:
- mchannel = self->peek_channel (event->channel);
- EDEBUG ("MidiChannel[%u]: %s %fHz (stamp:%llu)", event->channel,
- event->status == BSE_MIDI_NOTE_OFF ? "NoteOff" : "NotePressure",
- event->data.note.frequency, event->delta_time);
- if (mchannel)
- {
- gboolean sustained_note = event->status == BSE_MIDI_NOTE_OFF &&
- (BSE_GCONFIG (invert_sustain) ^
- (self->get_control (event->channel, BSE_MIDI_SIGNAL_CONTROL_64) >=
0.5));
- mchannel->adjust_note (event->delta_time,
- event->data.note.frequency, event->status,
- event->data.note.velocity, sustained_note, trans);
- }
- break;
- case BSE_MIDI_CONTROL_CHANGE:
- EDEBUG ("MidiChannel[%u]: Control %2u Value=%f (stamp:%llu)", event->channel,
- event->data.control.control, event->data.control.value, event->delta_time);
- process_midi_control_L (self, event->channel, event->delta_time,
- event->data.control.control, event->data.control.value,
- FALSE,
- trans);
- break;
- case BSE_MIDI_X_CONTINUOUS_CHANGE:
- EDEBUG ("MidiChannel[%u]: X Continuous Control %2u Value=%f (stamp:%llu)", event->channel,
- event->data.control.control, event->data.control.value, event->delta_time);
- process_midi_control_L (self, event->channel, event->delta_time,
- event->data.control.control, event->data.control.value,
- TRUE,
- trans);
- break;
- case BSE_MIDI_PROGRAM_CHANGE:
- EDEBUG ("MidiChannel[%u]: Program %u (Value=%f) (stamp:%llu)", event->channel,
- event->data.program, event->data.program / (gfloat) 0x7f, event->delta_time);
- update_midi_signal_L (self, event->channel, event->delta_time,
- BSE_MIDI_SIGNAL_PROGRAM, event->data.program / (gfloat) 0x7f,
- trans);
- break;
- case BSE_MIDI_CHANNEL_PRESSURE:
- EDEBUG ("MidiChannel[%u]: Channel Pressure Value=%f (stamp:%llu)", event->channel,
- event->data.intensity, event->delta_time);
- update_midi_signal_L (self, event->channel, event->delta_time,
- BSE_MIDI_SIGNAL_PRESSURE, event->data.intensity,
- trans);
- break;
- case BSE_MIDI_PITCH_BEND:
- EDEBUG ("MidiChannel[%u]: Pitch Bend Value=%f (stamp:%llu)", event->channel,
- event->data.pitch_bend, event->delta_time);
- update_midi_signal_L (self, event->channel, event->delta_time,
- BSE_MIDI_SIGNAL_PITCH_BEND, event->data.pitch_bend,
- trans);
- break;
- default:
- EDEBUG ("MidiChannel[%u]: Ignoring Event %u (stamp:%llu)", event->channel,
- event->status, event->delta_time);
- break;
+ switch (event->status)
+ {
+ case BSE_MIDI_NOTE_ON:
+ EDEBUG ("MidiChannel[%u]: NoteOn %fHz Velo=%f (stamp:%llu)", event->channel,
+ event->data.note.frequency, event->data.note.velocity, event->delta_time);
+ if (mchannel)
+ mchannel->start_note (event->delta_time,
+ event->data.note.frequency,
+ event->data.note.velocity,
+ trans);
+ else
+ sfi_diag ("ignoring note-on (%fHz) for foreign midi channel: %u", event->data.note.frequency,
event->channel);
+ break;
+ case BSE_MIDI_KEY_PRESSURE:
+ case BSE_MIDI_NOTE_OFF:
+ EDEBUG ("MidiChannel[%u]: %s %fHz (stamp:%llu)", event->channel,
+ event->status == BSE_MIDI_NOTE_OFF ? "NoteOff" : "NotePressure",
+ event->data.note.frequency, event->delta_time);
+ if (mchannel)
+ {
+ gboolean sustained_note = event->status == BSE_MIDI_NOTE_OFF &&
+ (BSE_GCONFIG (invert_sustain) ^
+ (self->get_control (event->channel, BSE_MIDI_SIGNAL_CONTROL_64)
= 0.5));
+ mchannel->adjust_note (event->delta_time,
+ event->data.note.frequency, event->status,
+ event->data.note.velocity, sustained_note, trans);
+ }
+ break;
+ case BSE_MIDI_CONTROL_CHANGE:
+ EDEBUG ("MidiChannel[%u]: Control %2u Value=%f (stamp:%llu)", event->channel,
+ event->data.control.control, event->data.control.value, event->delta_time);
+ process_midi_control_L (self, event->channel, event->delta_time,
+ event->data.control.control, event->data.control.value,
+ FALSE,
+ trans);
+ break;
+ case BSE_MIDI_X_CONTINUOUS_CHANGE:
+ EDEBUG ("MidiChannel[%u]: X Continuous Control %2u Value=%f (stamp:%llu)", event->channel,
+ event->data.control.control, event->data.control.value, event->delta_time);
+ process_midi_control_L (self, event->channel, event->delta_time,
+ event->data.control.control, event->data.control.value,
+ TRUE,
+ trans);
+ break;
+ case BSE_MIDI_PROGRAM_CHANGE:
+ EDEBUG ("MidiChannel[%u]: Program %u (Value=%f) (stamp:%llu)", event->channel,
+ event->data.program, event->data.program / (gfloat) 0x7f, event->delta_time);
+ update_midi_signal_L (self, event->channel, event->delta_time,
+ BSE_MIDI_SIGNAL_PROGRAM, event->data.program / (gfloat) 0x7f,
+ trans);
+ break;
+ case BSE_MIDI_CHANNEL_PRESSURE:
+ EDEBUG ("MidiChannel[%u]: Channel Pressure Value=%f (stamp:%llu)", event->channel,
+ event->data.intensity, event->delta_time);
+ update_midi_signal_L (self, event->channel, event->delta_time,
+ BSE_MIDI_SIGNAL_PRESSURE, event->data.intensity,
+ trans);
+ break;
+ case BSE_MIDI_PITCH_BEND:
+ EDEBUG ("MidiChannel[%u]: Pitch Bend Value=%f (stamp:%llu)", event->channel,
+ event->data.pitch_bend, event->delta_time);
+ update_midi_signal_L (self, event->channel, event->delta_time,
+ BSE_MIDI_SIGNAL_PITCH_BEND, event->data.pitch_bend,
+ trans);
+ break;
+ default:
+ EDEBUG ("MidiChannel[%u]: Ignoring Event %u (stamp:%llu)", event->channel,
+ event->status, event->delta_time);
+ break;
+ }
}
if (self->notifier)
{
diff --git a/bse/bsemidireceiver.hh b/bse/bsemidireceiver.hh
index 915de2c..d7a1de6 100644
--- a/bse/bsemidireceiver.hh
+++ b/bse/bsemidireceiver.hh
@@ -20,6 +20,10 @@ typedef void (*BseMidiControlHandler) (gpointer h
BseModule *const *modules,
gpointer user_data,
BseTrans *trans);
+typedef void (*BseMidiEventHandler) (gpointer handler_data,
+ BseModule *module,
+ const BseMidiEvent *event,
+ BseTrans *trans);
BseMidiReceiver* bse_midi_receiver_new (const gchar *receiver_name);
BseMidiReceiver* bse_midi_receiver_ref (BseMidiReceiver *self);
void bse_midi_receiver_unref (BseMidiReceiver *self);
@@ -53,6 +57,16 @@ void bse_midi_receiver_remove_control_handler (BseMidiReceiver *s
BseMidiControlHandler handler_func,
gpointer handler_data,
BseModule *module);
+void bse_midi_receiver_add_event_handler (BseMidiReceiver *self,
+ guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module);
+void bse_midi_receiver_remove_event_handler (BseMidiReceiver *self,
+ guint midi_channel,
+ BseMidiEventHandler handler_func,
+ gpointer handler_data,
+ BseModule *module);
BseModule* bse_midi_receiver_retrieve_mono_voice (BseMidiReceiver *self,
guint midi_channel,
BseTrans *trans);
diff --git a/bse/bseproject.cc b/bse/bseproject.cc
index e278a35..039c21b 100644
--- a/bse/bseproject.cc
+++ b/bse/bseproject.cc
@@ -16,6 +16,7 @@
#include "bsemidinotifier.hh"
#include "gslcommon.hh"
#include "bseengine.hh"
+#include "bsesoundfontrepo.h"
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
@@ -181,9 +182,15 @@ bse_project_init (BseProject *self,
self->midi_receiver = bse_midi_receiver_new ("BseProjectReceiver");
bse_midi_receiver_enter_farm (self->midi_receiver);
/* we always have a wave-repo */
- BseWaveRepo *wrepo = (BseWaveRepo*) bse_container_new_child (BSE_CONTAINER (self), BSE_TYPE_WAVE_REPO,
"uname", "Wave-Repository", NULL);
+ BseWaveRepo *wrepo = bse_container_new_child (BSE_CONTAINER (self), BSE_TYPE_WAVE_REPO,
+ "uname", "Wave-Repository",
+ NULL);
+ BseSoundFontRepo *sfrepo = bse_container_new_child (BSE_CONTAINER (self), BSE_TYPE_SOUND_FONT_REPO,
+ "uname", "Sound-Font-Repository",
+ NULL);
/* with fixed uname */
BSE_OBJECT_SET_FLAGS (wrepo, BSE_OBJECT_FLAG_FIXED_UNAME);
+ BSE_OBJECT_SET_FLAGS (sfrepo, BSE_OBJECT_FLAG_FIXED_UNAME);
}
static void
@@ -414,6 +421,13 @@ bse_project_retrieve_child (BseContainer *container,
g_warning ("%s: no wave-repo found in project\n", G_STRLOC);
return NULL; /* shouldn't happen */
}
+ else if (g_type_is_a (child_type, BSE_TYPE_SOUND_FONT_REPO)) /* and the same sound font repo */
+ {
+ BseSoundFontRepo *sfrepo = bse_project_get_sound_font_repo (self);
+ if (!sfrepo)
+ g_warning ("%s: no sound-font-repo found in project\n", G_STRLOC);
+ return BSE_ITEM (sfrepo);
+ }
else
{
BseItem *item = BSE_CONTAINER_CLASS (parent_class)->retrieve_child (container, child_type, uname);
@@ -633,6 +647,17 @@ bse_project_get_wave_repo (BseProject *self)
return NULL;
}
+BseSoundFontRepo*
+bse_project_get_sound_font_repo (BseProject *self)
+{
+ g_return_val_if_fail (BSE_IS_PROJECT (self), NULL);
+ GSList *slist;
+ for (slist = self->supers; slist; slist = slist->next)
+ if (BSE_IS_SOUND_FONT_REPO (slist->data))
+ return slist->data;
+ return NULL;
+}
+
BseSong*
bse_project_get_song (BseProject *self)
{
diff --git a/bse/bseproject.hh b/bse/bseproject.hh
index f4ebe16..48877b0 100644
--- a/bse/bseproject.hh
+++ b/bse/bseproject.hh
@@ -66,6 +66,7 @@ BseItem* bse_project_lookup_typed_item (BseProject *project,
GType item_type,
const gchar *uname);
BseWaveRepo* bse_project_get_wave_repo (BseProject *project);
+BseSoundFontRepo* bse_project_get_sound_font_repo (BseProject *project);
BseSong* bse_project_get_song (BseProject *project);
BseSNet* bse_project_create_intern_synth (BseProject *project,
const gchar *synth_name,
diff --git a/bse/bseproject.proc b/bse/bseproject.proc
index ff4a8d5..2dc1d52 100644
--- a/bse/bseproject.proc
+++ b/bse/bseproject.proc
@@ -6,6 +6,7 @@
#include <bse/bsesong.hh>
#include <bse/bseundostack.hh>
#include <bse/bsewaverepo.hh>
+#include <bse/bsesoundfontrepo.h>
#include <bse/bsecsynth.hh>
#include <bse/bsemidisynth.hh>
#include <bse/bsedatapocket.hh>
@@ -15,7 +16,6 @@
#include <bse/bseengine.hh>
#include "bsecxxplugin.hh"
-
AUTHORS = "Tim Janik <timj gtk org>";
LICENSE = "GNU Lesser General Public License";
@@ -286,6 +286,34 @@ METHOD (BseProject, get-wave-repo) {
return BSE_ERROR_NONE;
}
+METHOD (BseProject, get-sound-font-repo) {
+ HELP = "Get sound font repository for project";
+ IN = bse_param_spec_object ("project", "Project", "The project",
+ BSE_TYPE_PROJECT, SFI_PARAM_STANDARD);
+ OUT = bse_param_spec_object ("sfrepo", "Sound Font Repo", "The project's unique sound font repo",
+ BSE_TYPE_SOUND_FONT_REPO, SFI_PARAM_STANDARD);
+
+} BODY (BseProcedureClass *proc,
+ const GValue *in_values,
+ GValue *out_values)
+{
+ /* extract parameter values */
+ BseProject *project = bse_value_get_object (in_values++);
+ BseSoundFontRepo *sfrepo = NULL;
+
+ /* check parameters */
+ if (!BSE_IS_PROJECT (project))
+ return BSE_ERROR_PROC_PARAM_INVAL;
+
+ /* action */
+ sfrepo = bse_project_get_sound_font_repo (project);
+
+ /* set output parameters */
+ bse_value_set_object (out_values++, sfrepo);
+
+ return BSE_ERROR_NONE;
+}
+
METHOD (BseProject, get-data-pocket) {
HELP = "Retrieve a specifically named data pocket for this project";
IN = bse_param_spec_object ("project", "Project", "The project",
diff --git a/bse/bsesoundfont.c b/bse/bsesoundfont.c
new file mode 100644
index 0000000..096d749
--- /dev/null
+++ b/bse/bsesoundfont.c
@@ -0,0 +1,394 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1997-1999, 2000-2005 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bsesoundfont.h"
+#include "bsesoundfontrepo.h"
+#include "bsesoundfontpreset.h"
+#include "bsemain.h"
+#include "bsestorage.h"
+#include "bseprocedure.h"
+#include "gsldatahandle.h"
+#include "bseserver.h"
+#include "bseloader.h"
+
+#include <string.h>
+
+#define parse_or_return bse_storage_scanner_parse_or_return
+
+enum {
+ PARAM_0,
+ PARAM_FILE_NAME,
+};
+
+/* --- prototypes --- */
+
+
+/* --- variables --- */
+static GTypeClass *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);
+}
+
+BseErrorType
+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);
+ BseErrorType 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 = g_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;
+}
+
+BseErrorType
+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 SfiTokenType
+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;
+ BseErrorType 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 ')';
+ }
+ parse_or_return (scanner, ')');
+ error = bse_sound_font_load_blob (sound_font, blob, FALSE);
+ if (error)
+ 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 = 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, self->presets->data);
+
+ /* chain parent class' handler */
+ BSE_CONTAINER_CLASS (parent_class)->release_children (container);
+}
+
+
+static void
+bse_sound_font_class_init (BseSoundFontClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ BseObjectClass *object_class = BSE_OBJECT_CLASS (class);
+ BseContainerClass *container_class = BSE_CONTAINER_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ 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);
+}
diff --git a/bse/bsesoundfont.h b/bse/bsesoundfont.h
new file mode 100644
index 0000000..6705f8e
--- /dev/null
+++ b/bse/bsesoundfont.h
@@ -0,0 +1,59 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1997-1999, 2000-2005 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef __BSE_SOUND_FONT_H__
+#define __BSE_SOUND_FONT_H__
+
+#include <bse/bsecontainer.h>
+#include <bse/bsestorage.h>
+
+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))
+
+
+/* --- BseSoundFont --- */
+struct _BseSoundFont
+{
+ BseContainer parent_object;
+ BseStorageBlob *blob;
+ int sfont_id;
+ BseSoundFontRepo *sfrepo;
+ GList *presets;
+};
+struct _BseSoundFontClass
+{
+ BseContainerClass parent_class;
+};
+
+
+/* --- prototypes -- */
+BseErrorType bse_sound_font_load_blob (BseSoundFont *sound_font,
+ BseStorageBlob *blob,
+ gboolean init_presets);
+void bse_sound_font_unload (BseSoundFont *sound_font);
+BseErrorType bse_sound_font_reload (BseSoundFont *sound_font);
+
+G_END_DECLS
+
+#endif /* __BSE_SOUND_FONT_H__ */
diff --git a/bse/bsesoundfontosc.c b/bse/bsesoundfontosc.c
new file mode 100644
index 0000000..1ba2b9e
--- /dev/null
+++ b/bse/bsesoundfontosc.c
@@ -0,0 +1,617 @@
+/* BseSoundFontOsc - BSE Fluid Synth
+ * Copyright (C) 1999-2002 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bsesoundfontosc.h"
+
+#include <bse/bsecategories.h>
+#include <bse/bseengine.h>
+#include <bse/bseproject.h>
+#include <bse/bsesoundfontrepo.h>
+#include <bse/bsesoundfont.h>
+#include <bse/bsesnet.h>
+#include <bse/bsemidireceiver.h>
+#include "gslcommon.h"
+
+#include <string.h>
+
+/* --- parameters --- */
+enum
+{
+ PARAM_0,
+ PARAM_PRESET
+};
+
+
+/* --- prototypes --- */
+static void bse_sound_font_osc_init (BseSoundFontOsc *sound_font_osc);
+static void bse_sound_font_osc_class_init (BseSoundFontOscClass *class);
+static void bse_sound_font_osc_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void bse_sound_font_osc_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void bse_sound_font_osc_get_candidates (BseItem *item,
+ guint param_id,
+ BsePropertyCandidates *pc,
+ GParamSpec *pspec);
+static void bse_sound_font_osc_context_create (BseSource *source,
+ guint context_handle,
+ BseTrans *trans);
+static void bse_sound_font_osc_context_dismiss (BseSource *source,
+ guint context_handle,
+ BseTrans *trans);
+static void bse_sound_font_osc_update_modules (BseSoundFontOsc *sound_font_osc,
+ BseTrans *trans);
+static void bse_sound_font_osc_dispose (GObject *object);
+static void bse_sound_font_osc_finalize (GObject *object);
+
+
+/* --- variables --- */
+static gpointer parent_class = NULL;
+
+
+/* --- functions --- */
+BSE_BUILTIN_TYPE (BseSoundFontOsc)
+{
+ static const GTypeInfo type_info = {
+ sizeof (BseSoundFontOscClass),
+
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) bse_sound_font_osc_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL /* class_data */,
+
+ sizeof (BseSoundFontOsc),
+ 0 /* n_preallocs */,
+ (GInstanceInitFunc) bse_sound_font_osc_init,
+ };
+ GType type_id;
+
+ type_id = bse_type_register_static (BSE_TYPE_SOURCE,
+ "BseSoundFontOsc",
+ "This internal module wraps fluid synth which plays sound font
contents",
+ __FILE__, __LINE__,
+ &type_info);
+ return type_id;
+}
+
+static void
+bse_sound_font_osc_class_init (BseSoundFontOscClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ BseObjectClass *object_class = BSE_OBJECT_CLASS (class);
+ BseSourceClass *source_class = BSE_SOURCE_CLASS (class);
+ BseItemClass *item_class = BSE_ITEM_CLASS (class);
+ guint ochannel;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = bse_sound_font_osc_set_property;
+ gobject_class->get_property = bse_sound_font_osc_get_property;
+ gobject_class->finalize = bse_sound_font_osc_finalize;
+ gobject_class->dispose = bse_sound_font_osc_dispose;
+
+ item_class->get_candidates = bse_sound_font_osc_get_candidates;
+
+ source_class->context_create = bse_sound_font_osc_context_create;
+ source_class->context_dismiss = bse_sound_font_osc_context_dismiss;
+
+ bse_object_class_add_param (object_class, _("Sound Font Preset"),
+ PARAM_PRESET,
+ bse_param_spec_object ("preset", _("Preset"), _("Sound Font Preset to be used
during replay"),
+ BSE_TYPE_SOUND_FONT_PRESET, SFI_PARAM_STANDARD));
+
+ ochannel = bse_source_class_add_ochannel (source_class, "left-out", _("Left Out"), _("Output of the fluid
synth soundfont synthesizer"));
+ g_assert (ochannel == BSE_SOUND_FONT_OSC_OCHANNEL_LEFT_OUT);
+ ochannel = bse_source_class_add_ochannel (source_class, "right-out", _("Right Out"), _("Output of the
fluid synth soundfont synthesizer"));
+ g_assert (ochannel == BSE_SOUND_FONT_OSC_OCHANNEL_RIGHT_OUT);
+ ochannel = bse_source_class_add_ochannel (source_class, "done-out", _("Done Out"), _("Done Output"));
+ g_assert (ochannel == BSE_SOUND_FONT_OSC_OCHANNEL_DONE_OUT);
+}
+
+static void
+bse_sound_font_osc_init (BseSoundFontOsc *self)
+{
+ memset (&self->config, 0, sizeof (self->config));
+ self->preset = NULL;
+}
+
+static void
+bse_sound_font_osc_dispose (GObject *object)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (object);
+
+ if (self->config.sfrepo)
+ {
+ bse_sound_font_repo_remove_osc (self->config.sfrepo, self->config.osc_id);
+
+ self->config.sfrepo = NULL;
+ }
+
+ /* chain parent class' handler */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+bse_sound_font_osc_finalize (GObject *object)
+{
+ //BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (object);
+
+ /* chain parent class' handler */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static BseSoundFontRepo*
+get_sfrepo (BseSoundFontOsc *self)
+{
+ if (!self->config.sfrepo)
+ {
+ BseProject *project = bse_item_get_project (BSE_ITEM (self));
+ if (project)
+ {
+ self->config.sfrepo = bse_project_get_sound_font_repo (project);
+ self->config.osc_id = bse_sound_font_repo_add_osc (self->config.sfrepo, self);
+ }
+ else
+ {
+ g_warning ("BseSoundFontOsc: could not find sfrepo\n");
+ self->config.sfrepo = NULL;
+ }
+ }
+ return self->config.sfrepo;
+}
+
+static void
+bse_sound_font_osc_uncross_preset (BseItem *owner,
+ BseItem *ref_item)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (owner);
+ bse_item_set (self, "preset", NULL, NULL);
+}
+
+
+static void
+bse_sound_font_osc_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (object);
+
+ switch (param_id)
+ {
+ BseSoundFontPreset *preset;
+ case PARAM_PRESET:
+ preset = bse_value_get_object (value);
+ if (preset != self->preset)
+ {
+ self->preset = preset;
+ if (self->preset)
+ {
+ bse_item_cross_link (BSE_ITEM (self), BSE_ITEM (self->preset),
bse_sound_font_osc_uncross_preset);
+ bse_object_proxy_notifies (self->preset, self, "notify::preset");
+ self->config.sfont_id = BSE_SOUND_FONT (BSE_ITEM (self->preset)->parent)->sfont_id;
+ self->config.bank = self->preset->bank;
+ self->config.program = self->preset->program;
+ self->config.update_preset++;
+ bse_sound_font_osc_update_modules (self, NULL);
+ }
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, param_id, pspec);
+ break;
+ }
+}
+
+static void
+bse_sound_font_osc_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (object);
+
+ switch (param_id)
+ {
+ case PARAM_PRESET:
+ bse_value_set_object (value, self->preset);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+bse_sound_font_osc_get_candidates (BseItem *item,
+ guint param_id,
+ BsePropertyCandidates *pc,
+ GParamSpec *pspec)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (item);
+ switch (param_id)
+ {
+ case PARAM_PRESET:
+ bse_property_candidate_relabel (pc, _("Available Presets"), _("List of available sound font presets to
choose as fluid synth preset"));
+ bse_sound_font_repo_list_all_presets (get_sfrepo (self), pc->items);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (self, param_id, pspec);
+ break;
+ }
+}
+
+
+typedef struct
+{
+ BseSoundFontOscConfig config;
+ int last_update_preset;
+ guint64 n_silence_samples; // for done detection
+} SoundFontOscModule;
+
+static void
+bse_sound_font_osc_update_modules (BseSoundFontOsc *sound_font_osc,
+ BseTrans *trans)
+{
+ get_sfrepo (sound_font_osc);
+
+ if (BSE_SOURCE_PREPARED (sound_font_osc))
+ {
+ bse_source_update_modules (BSE_SOURCE (sound_font_osc),
+ G_STRUCT_OFFSET (SoundFontOscModule, config),
+ &sound_font_osc->config,
+ sizeof (sound_font_osc->config),
+ trans);
+ }
+}
+
+static void
+sound_font_osc_reset (BseModule *module)
+{
+ SoundFontOscModule *flmod = module->user_data;
+
+ flmod->last_update_preset = -1;
+}
+
+/* process_fluid is only called once per block, not once per module
+ */
+static void
+process_fluid_L (BseSoundFontRepo *sfrepo,
+ fluid_synth_t *fluid_synth,
+ guint64 now_tick_stamp)
+{
+ float **channel_values_left = g_alloca (sfrepo->n_fluid_channels * sizeof (float *));
+ float **channel_values_right = g_alloca (sfrepo->n_fluid_channels * sizeof (float *));
+ float null_fx[BSE_STREAM_MAX_VALUES];
+ float *channel_fx_null[2] = { null_fx, null_fx };
+ int i;
+
+ g_return_if_fail (now_tick_stamp > sfrepo->channel_values_tick_stamp);
+ sfrepo->channel_values_tick_stamp = now_tick_stamp;
+
+ /* Sample precise timing: If events don't occur at block boundary, the block
+ is partially calculated, then the event is executed, and then the rest of
+ the block (until the next event) is calculated, and so on */
+ for (i = 0; i < sfrepo->n_fluid_channels; i++)
+ {
+ channel_values_left[i] = sfrepo->channel_values_left[i];
+ channel_values_right[i] = sfrepo->channel_values_right[i];
+ }
+ guint values_remaining = bse_engine_block_size();
+ while (values_remaining)
+ {
+ /* get 1st event tick stamp */
+ BseFluidEvent *event = NULL;
+ guint64 event_tick_stamp;
+ if (sfrepo->fluid_events)
+ {
+ event = (BseFluidEvent *) sfrepo->fluid_events->data;
+ event_tick_stamp = event->tick_stamp;
+ }
+ else
+ {
+ /* if no event is present, the earliest event that can occur is after this block */
+ event_tick_stamp = now_tick_stamp + values_remaining;
+ }
+ if (event_tick_stamp <= now_tick_stamp) /* past or present event -> process it */
+ {
+ switch (event->command)
+ {
+ case BSE_MIDI_NOTE_ON: fluid_synth_noteon (fluid_synth, event->channel, event->arg1,
event->arg2);
+ sfrepo->n_silence_samples[event->channel] = 0;
+ break;
+ case BSE_MIDI_NOTE_OFF: fluid_synth_noteoff (fluid_synth, event->channel, event->arg1);
+ break;
+ case BSE_MIDI_PITCH_BEND: fluid_synth_pitch_bend (fluid_synth, event->channel, event->arg1);
+ break;
+ case BSE_FLUID_SYNTH_PROGRAM_SELECT:
+ fluid_synth_program_select (fluid_synth, event->channel,
+ event->sfont_id,
+ event->arg1, event->arg2);
+ break;
+ case BSE_MIDI_X_CONTINUOUS_CHANGE:
+ fluid_synth_cc (fluid_synth, event->channel,
+ event->arg1, event->arg2);
+ break;
+ }
+ sfrepo->fluid_events = sfi_ring_remove_node (sfrepo->fluid_events, sfrepo->fluid_events);
+ g_free (event);
+ }
+ else /* future event tick stamp: process audio until
then */
+ {
+ gint64 values_todo = MIN (values_remaining, event_tick_stamp - now_tick_stamp);
+ fluid_synth_nwrite_float (fluid_synth, values_todo,
+ channel_values_left, channel_values_right,
+ channel_fx_null, channel_fx_null);
+ values_remaining -= values_todo;
+ now_tick_stamp += values_todo;
+ for (i = 0; i < sfrepo->n_fluid_channels; i++) /* increment fluid synth output buffer
pointers */
+ {
+ channel_values_left[i] += values_todo;
+ channel_values_right[i] += values_todo;
+ }
+ }
+ }
+}
+
+static void
+sound_font_osc_process (BseModule *module,
+ guint n_values)
+{
+ SoundFontOscModule *flmod = module->user_data;
+ BseSoundFontRepo *sfrepo = flmod->config.sfrepo;
+ fluid_synth_t *fluid_synth = bse_sound_font_repo_lock_fluid_synth (sfrepo);
+ guint i;
+ if (flmod->config.update_preset != flmod->last_update_preset)
+ {
+ fluid_synth_program_select (fluid_synth, flmod->config.sfrepo->channel_map[flmod->config.osc_id],
+ flmod->config.sfont_id, flmod->config.bank,
flmod->config.program);
+ flmod->last_update_preset = flmod->config.update_preset;
+ }
+ gint64 now_tick_stamp = GSL_TICK_STAMP;
+ if (sfrepo->channel_values_tick_stamp != now_tick_stamp)
+ process_fluid_L (sfrepo, fluid_synth, now_tick_stamp);
+
+ float *left_output = sfrepo->channel_values_left[sfrepo->channel_map[flmod->config.osc_id]];
+ float *right_output = sfrepo->channel_values_right[sfrepo->channel_map[flmod->config.osc_id]];
+
+ int delta = bse_module_tick_stamp (module) - now_tick_stamp;
+ if (delta + n_values <= bse_engine_block_size()) /* paranoid check, should always pass */
+ {
+ left_output += delta;
+ right_output += delta;
+ BSE_MODULE_OSTREAM (module, BSE_SOUND_FONT_OSC_OCHANNEL_LEFT_OUT).values = left_output;
+ BSE_MODULE_OSTREAM (module, BSE_SOUND_FONT_OSC_OCHANNEL_RIGHT_OUT).values = right_output;
+ }
+ else
+ {
+ g_warning (G_STRLOC ": access past end of channel_values buffer");
+ }
+ if (BSE_MODULE_OSTREAM (module, BSE_SOUND_FONT_OSC_OCHANNEL_DONE_OUT).connected)
+ {
+ for (i = 0; i < n_values && left_output[i] == 0.0 && right_output[i] == 0.0; i++)
+ ;
+ if (i == n_values)
+ sfrepo->n_silence_samples[sfrepo->channel_map[flmod->config.osc_id]] += n_values;
+ else
+ sfrepo->n_silence_samples[sfrepo->channel_map[flmod->config.osc_id]] = 0;
+ float done = (sfrepo->n_silence_samples[sfrepo->channel_map[flmod->config.osc_id]] > 1024 &&
sfrepo->fluid_events == NULL) ? 1.0 : 0.0;
+ BSE_MODULE_OSTREAM (module, BSE_SOUND_FONT_OSC_OCHANNEL_DONE_OUT).values = bse_engine_const_values
(done);
+ }
+
+ bse_sound_font_repo_unlock_fluid_synth (sfrepo);
+}
+
+static int
+event_cmp (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ const BseFluidEvent *event1 = (const BseFluidEvent *) a;
+ const BseFluidEvent *event2 = (const BseFluidEvent *) b;
+
+ if (event1->tick_stamp < event2->tick_stamp)
+ return -1;
+ return (event1->tick_stamp > event2->tick_stamp);
+}
+
+static void
+sound_font_osc_process_midi (gpointer null,
+ BseModule *module,
+ const BseMidiEvent *event,
+ BseTrans *trans)
+{
+ SoundFontOscModule *flmod = module->user_data;
+ bse_sound_font_repo_lock_fluid_synth (flmod->config.sfrepo);
+ int note = bse_note_from_freq (BSE_MUSICAL_TUNING_12_TET, event->data.note.frequency);
+ BseFluidEvent *fluid_event = NULL;
+ switch (event->status)
+ {
+ case BSE_MIDI_NOTE_ON:
+ fluid_event = g_new0 (BseFluidEvent, 1);
+ fluid_event->command = BSE_MIDI_NOTE_ON;
+ fluid_event->arg1 = note;
+ fluid_event->arg2 = event->data.note.velocity * 127;
+ break;
+ case BSE_MIDI_NOTE_OFF:
+ fluid_event = g_new0 (BseFluidEvent, 1);
+ fluid_event->command = BSE_MIDI_NOTE_OFF;
+ fluid_event->arg1 = note;
+ break;
+ case BSE_MIDI_PITCH_BEND:
+ fluid_event = g_new0 (BseFluidEvent, 1);
+ fluid_event->command = BSE_MIDI_PITCH_BEND;
+ /* since midi uses 14 bits, the range is 0x0000 ... 0x3fff
+ * however, since beast uses -1 ... 0 ... 1, we use the formula
+ * below with an output range of 0x0000 ... 0x4000 (but fluid synth
+ * seems to accept these values without trouble) - its also the
+ * inverse of whats done to the input in bsemididecoder.c
+ */
+ fluid_event->arg1 = (event->data.pitch_bend * 0x2000) + 0x2000;
+ break;
+ case BSE_MIDI_CONTROL_CHANGE:
+ case BSE_MIDI_X_CONTINUOUS_CHANGE:
+ fluid_event = g_new0 (BseFluidEvent, 1);
+ fluid_event->command = BSE_MIDI_X_CONTINUOUS_CHANGE;
+ fluid_event->arg1 = event->data.control.control;
+ /* we do the inverse of what the BEAST midi file reading code does;
+ * this means we should be able to replay midi files without loosing
+ * any information - however, since midi information has no sign,
+ * we truncate negative numbers to zero
+ *
+ * FIXME: it would be possible to do an almost loss free conversion
+ * if the beast representation of controls would be more MIDI like
+ */
+ fluid_event->arg2 = CLAMP (event->data.control.value * 127, 0, 127);
+ break;
+ case BSE_MIDI_PROGRAM_CHANGE:
+ /* programs should be set at track level, and are thus not changeable here */
+ break;
+ default:
+ printf ("BseSoundFontOsc: unhandled status %02x\n", event->status);
+ break;
+ }
+ if (fluid_event)
+ {
+ fluid_event->tick_stamp = event->delta_time;
+ fluid_event->channel = flmod->config.sfrepo->channel_map[flmod->config.osc_id];
+ flmod->config.sfrepo->fluid_events = sfi_ring_insert_sorted (flmod->config.sfrepo->fluid_events,
fluid_event, event_cmp, NULL);
+ }
+ bse_sound_font_repo_unlock_fluid_synth (flmod->config.sfrepo);
+}
+
+typedef struct
+{
+ BseMidiReceiver *midi_receiver;
+ guint midi_channel;
+ BseModule *module;
+} EventHandlerSetup;
+
+static void
+event_handler_setup_func (BseModule *module,
+ void *ehs_data)
+{
+ EventHandlerSetup *ehs = (EventHandlerSetup *)ehs_data;
+ bse_midi_receiver_add_event_handler (ehs->midi_receiver,
+ ehs->midi_channel,
+ sound_font_osc_process_midi,
+ NULL,
+ ehs->module);
+
+ /* setup program before first midi event */
+ SoundFontOscModule *flmod = module->user_data;
+
+ BseFluidEvent *fluid_event = g_new0 (BseFluidEvent, 1);
+ fluid_event->command = BSE_FLUID_SYNTH_PROGRAM_SELECT;
+ fluid_event->channel = flmod->config.sfrepo->channel_map[flmod->config.osc_id];
+ fluid_event->arg1 = flmod->config.bank;
+ fluid_event->arg2 = flmod->config.program;
+ fluid_event->sfont_id = flmod->config.sfont_id;
+ fluid_event->tick_stamp = 0; /* now */
+ flmod->config.sfrepo->fluid_events = sfi_ring_insert_sorted (flmod->config.sfrepo->fluid_events,
fluid_event, event_cmp, NULL);
+}
+
+static void
+bse_sound_font_osc_context_create (BseSource *source,
+ guint context_handle,
+ BseTrans *trans)
+{
+ static const BseModuleClass sound_font_osc_class = {
+ 0, /* n_istreams */
+ 0, /* n_jstreams */
+ BSE_SOUND_FONT_OSC_N_OCHANNELS, /* n_ostreams */
+ sound_font_osc_process, /* process */
+ NULL, /* process_defer */
+ sound_font_osc_reset, /* reset */
+ (BseModuleFreeFunc) g_free, /* free */
+ BSE_COST_CHEAP, /* flags */
+ };
+ SoundFontOscModule *sound_font_osc = g_new0 (SoundFontOscModule, 1);
+ BseModule *module;
+
+ module = bse_module_new (&sound_font_osc_class, sound_font_osc);
+
+ /* setup module i/o streams with BseSource i/o channels */
+ bse_source_set_context_module (source, context_handle, module);
+
+ /* commit module to engine */
+ bse_trans_add (trans, bse_job_integrate (module));
+
+ /* chain parent class' handler */
+ BSE_SOURCE_CLASS (parent_class)->context_create (source, context_handle, trans);
+
+ /* update (initialize) module data */
+ bse_sound_font_osc_update_modules (BSE_SOUND_FONT_OSC (source), trans);
+
+ /* setup midi event handler */
+ EventHandlerSetup *ehs = g_new0 (EventHandlerSetup, 1);
+ BseMidiContext mc = bse_snet_get_midi_context (bse_item_get_snet (BSE_ITEM (source)), context_handle);
+ ehs->midi_receiver = mc.midi_receiver;
+ ehs->midi_channel = mc.midi_channel;
+ ehs->module = module;
+ bse_trans_add (trans, bse_job_access (module, event_handler_setup_func, ehs, g_free));
+
+ /* reset fluid synth if necessary */
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (source);
+ fluid_synth_t *fluid_synth = bse_sound_font_repo_lock_fluid_synth (self->config.sfrepo);
+ if (self->config.sfrepo->n_channel_oscs_active == 0)
+ fluid_synth_system_reset (fluid_synth);
+ self->config.sfrepo->n_channel_oscs_active++;
+ bse_sound_font_repo_unlock_fluid_synth (self->config.sfrepo);
+}
+
+static void
+bse_sound_font_osc_context_dismiss (BseSource *source,
+ guint context_handle,
+ BseTrans *trans)
+{
+ BseSoundFontOsc *self = BSE_SOUND_FONT_OSC (source);
+ BseModule *module = bse_source_get_context_omodule (source, context_handle);
+ BseMidiContext mc = bse_snet_get_midi_context (bse_item_get_snet (BSE_ITEM (source)), context_handle);
+ bse_midi_receiver_remove_event_handler (mc.midi_receiver,
+ mc.midi_channel,
+ sound_font_osc_process_midi,
+ NULL,
+ module);
+ /* remove old events from the event queue */
+ bse_sound_font_repo_lock_fluid_synth (self->config.sfrepo);
+ SfiRing *fluid_events = self->config.sfrepo->fluid_events;
+ SfiRing *node = fluid_events;
+ while (node)
+ {
+ SfiRing *next_node = sfi_ring_walk (node, fluid_events);
+ BseFluidEvent *event = node->data;
+ if (event->channel == self->config.sfrepo->channel_map[self->config.osc_id])
+ {
+ g_free (event);
+ fluid_events = sfi_ring_remove_node (fluid_events, node);
+ }
+ node = next_node;
+ }
+ self->config.sfrepo->n_channel_oscs_active--;
+ self->config.sfrepo->fluid_events = fluid_events;
+ bse_sound_font_repo_unlock_fluid_synth (self->config.sfrepo);
+ /* chain parent class' handler */
+ BSE_SOURCE_CLASS (parent_class)->context_dismiss (source, context_handle, trans);
+}
diff --git a/bse/bsesoundfontosc.h b/bse/bsesoundfontosc.h
new file mode 100644
index 0000000..395e217
--- /dev/null
+++ b/bse/bsesoundfontosc.h
@@ -0,0 +1,78 @@
+/* BseSoundFontOsc - BSE Fluid Synth sound font player
+ * Copyright (C) 1999-2002 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef __BSE_SOUND_FONT_OSC_H__
+#define __BSE_SOUND_FONT_OSC_H__
+
+#include <bse/bsesource.h>
+#include <bse/bsesoundfontpreset.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+
+/* --- object type macros --- */
+#define BSE_TYPE_SOUND_FONT_OSC (BSE_TYPE_ID (BseSoundFontOsc))
+#define BSE_SOUND_FONT_OSC(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), BSE_TYPE_SOUND_FONT_OSC,
BseSoundFontOsc))
+#define BSE_SOUND_FONT_OSC_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
BSE_TYPE_SOUND_FONT_OSC, BseSoundFontOscClass))
+#define BSE_IS_SOUND_FONT_OSC(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), BSE_TYPE_SOUND_FONT_OSC))
+#define BSE_IS_SOUND_FONT_OSC_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), BSE_TYPE_SOUND_FONT_OSC))
+#define BSE_SOUND_FONT_OSC_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), BSE_TYPE_SOUND_FONT_OSC,
BseSoundFontOscClass))
+
+enum
+{
+ BSE_SOUND_FONT_OSC_OCHANNEL_LEFT_OUT,
+ BSE_SOUND_FONT_OSC_OCHANNEL_RIGHT_OUT,
+ BSE_SOUND_FONT_OSC_OCHANNEL_DONE_OUT,
+ BSE_SOUND_FONT_OSC_N_OCHANNELS
+};
+
+/* --- BseSoundFontOsc source --- */
+typedef struct _BseSoundFontOsc BseSoundFontOsc;
+typedef struct _BseSoundFontOscClass BseSoundFontOscClass;
+typedef struct _BseSoundFontOscConfig BseSoundFontOscConfig;
+struct _BseSoundFontOscConfig
+{
+ int osc_id;
+ int sfont_id;
+ int bank;
+ int program;
+ BseSoundFontRepo *sfrepo;
+
+ int update_preset; /* preset changed indicator */
+};
+struct _BseSoundFontOsc
+{
+ BseSource parent_object;
+ BseSoundFontPreset *preset;
+ BseSoundFontOscConfig config;
+};
+struct _BseSoundFontOscClass
+{
+ BseSourceClass parent_class;
+};
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __BSE_SOUND_FONT_OSC_H__ */
diff --git a/bse/bsesoundfontpreset.c b/bse/bsesoundfontpreset.c
new file mode 100644
index 0000000..9de08b3
--- /dev/null
+++ b/bse/bsesoundfontpreset.c
@@ -0,0 +1,195 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1997-1999, 2000-2005 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bsesoundfontpreset.h"
+#include "bsemain.h"
+#include "bsestorage.h"
+#include "bseprocedure.h"
+#include "gsldatahandle.h"
+#include "bseserver.h"
+#include "bseloader.h"
+
+#include <string.h>
+#include <fluidsynth.h>
+
+#define parse_or_return bse_storage_scanner_parse_or_return
+
+/* --- variables --- */
+static GTypeClass *parent_class = NULL;
+static GQuark quark_program = 0;
+static GQuark quark_bank = 0;
+
+
+/* --- functions --- */
+static void
+bse_sound_font_preset_init (BseSoundFontPreset *sound_font_preset)
+{
+ // init members here
+}
+
+void
+bse_sound_font_preset_init_preset (BseSoundFontPreset *self,
+ fluid_preset_t *fluid_preset)
+{
+ self->bank = fluid_preset->get_banknum (fluid_preset);
+ self->program = fluid_preset->get_num (fluid_preset);
+}
+
+static void
+bse_sound_font_preset_store_private (BseObject *object,
+ BseStorage *storage)
+{
+ BseSoundFontPreset *self = BSE_SOUND_FONT_PRESET (object);
+ /* chain parent class' handler */
+ BSE_OBJECT_CLASS (parent_class)->store_private (object, storage);
+
+ bse_storage_break (storage);
+ bse_storage_printf (storage, "(bank %d)", self->bank);
+ bse_storage_break (storage);
+ bse_storage_printf (storage, "(program %d)", self->program);
+}
+
+static SfiTokenType
+bse_sound_font_preset_restore_private (BseObject *object,
+ BseStorage *storage,
+ GScanner *scanner)
+{
+ BseSoundFontPreset *sound_font_preset = BSE_SOUND_FONT_PRESET (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_program)
+ {
+ g_scanner_get_next_token (scanner); /* eat quark identifier */
+ parse_or_return (scanner, G_TOKEN_INT);
+ sound_font_preset->program = scanner->value.v_int;
+ parse_or_return (scanner, ')');
+ expected_token = G_TOKEN_NONE; /* got ')' */
+ }
+ else if (quark == quark_bank)
+ {
+ g_scanner_get_next_token (scanner); /* eat quark identifier */
+ parse_or_return (scanner, G_TOKEN_INT);
+ sound_font_preset->bank = scanner->value.v_int;
+ parse_or_return (scanner, ')');
+ 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_preset_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_preset_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ // BseSoundFontPreset *sound_font_preset = BSE_SOUND_FONT_PRESET (object);
+ switch (param_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+bse_sound_font_preset_dispose (GObject *object)
+{
+ // BseSoundFontPreset *sound_font_preset = BSE_SOUND_FONT_PRESET (object);
+ // bse_sound_font_preset_clear (sound_font_preset);
+
+ /* chain parent class' handler */
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+bse_sound_font_preset_finalize (GObject *object)
+{
+ // BseSoundFontPreset *sound_font_preset = BSE_SOUND_FONT_PRESET (object);
+ // bse_sound_font_preset_clear (sound_font_preset);
+
+ /* chain parent class' handler */
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+bse_sound_font_preset_class_init (BseSoundFontPresetClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ BseObjectClass *object_class = BSE_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->set_property = bse_sound_font_preset_set_property;
+ gobject_class->get_property = bse_sound_font_preset_get_property;
+ gobject_class->dispose = bse_sound_font_preset_dispose;
+ gobject_class->finalize = bse_sound_font_preset_finalize;
+
+ object_class->store_private = bse_sound_font_preset_store_private;
+ object_class->restore_private = bse_sound_font_preset_restore_private;
+
+ quark_program = g_quark_from_static_string ("program");
+ quark_bank = g_quark_from_static_string ("bank");
+}
+
+BSE_BUILTIN_TYPE (BseSoundFontPreset)
+{
+ static const GTypeInfo sound_font_preset_info = {
+ sizeof (BseSoundFontPresetClass),
+
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) bse_sound_font_preset_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL /* class_data */,
+
+ sizeof (BseSoundFontPreset),
+ 0 /* n_preallocs */,
+ (GInstanceInitFunc) bse_sound_font_preset_init,
+ };
+
+ return bse_type_register_static (BSE_TYPE_ITEM,
+ "BseSoundFontPreset",
+ "BSE sound_font_preset type",
+ __FILE__, __LINE__,
+ &sound_font_preset_info);
+}
diff --git a/bse/bsesoundfontpreset.h b/bse/bsesoundfontpreset.h
new file mode 100644
index 0000000..a90b931
--- /dev/null
+++ b/bse/bsesoundfontpreset.h
@@ -0,0 +1,54 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1997-1999, 2000-2005 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef __BSE_SOUND_FONT_PRESET_H__
+#define __BSE_SOUND_FONT_PRESET_H__
+
+#include <bse/bseitem.h>
+#include <fluidsynth.h>
+
+G_BEGIN_DECLS
+
+/* --- BSE type macros --- */
+#define BSE_TYPE_SOUND_FONT_PRESET (BSE_TYPE_ID (BseSoundFontPreset))
+#define BSE_SOUND_FONT_PRESET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object),
BSE_TYPE_SOUND_FONT_PRESET, BseSoundFontPreset))
+#define BSE_SOUND_FONT_PRESET_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class),
BSE_TYPE_SOUND_FONT_PRESET, BseSoundFontPresetClass))
+#define BSE_IS_SOUND_FONT_PRESET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object),
BSE_TYPE_SOUND_FONT_PRESET))
+#define BSE_IS_SOUND_FONT_PRESET_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class),
BSE_TYPE_SOUND_FONT_PRESET))
+#define BSE_SOUND_FONT_PRESET_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object),
BSE_TYPE_SOUND_FONT_PRESET, BseSoundFontPresetClass))
+
+
+/* --- BseSoundFontPreset --- */
+struct _BseSoundFontPreset
+{
+ BseItem parent_object;
+ int program;
+ int bank;
+};
+struct _BseSoundFontPresetClass
+{
+ BseItemClass parent_class;
+};
+
+
+/* --- prototypes -- */
+void bse_sound_font_preset_init_preset (BseSoundFontPreset *self,
+ fluid_preset_t *fluid_preset);
+
+G_END_DECLS
+
+#endif /* __BSE_SOUND_FONT_PRESET_H__ */
diff --git a/bse/bsesoundfontrepo.c b/bse/bsesoundfontrepo.c
new file mode 100644
index 0000000..00c321b
--- /dev/null
+++ b/bse/bsesoundfontrepo.c
@@ -0,0 +1,393 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1996-1999, 2000-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include "bsesoundfontrepo.h"
+#include "bsesoundfont.h"
+#include "bsesoundfontpreset.h"
+#include "bsedefs.h"
+#include "bseblockutils.hh"
+
+
+/* --- parameters --- */
+enum
+{
+ PARAM_0,
+};
+
+
+/* --- prototypes --- */
+static void bse_sound_font_repo_class_init (BseSoundFontRepoClass *class);
+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 GTypeClass *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_class_init (BseSoundFontRepoClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ BseContainerClass *container_class = BSE_CONTAINER_CLASS (class);
+ BseSourceClass *source_class = BSE_SOURCE_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ 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;
+
+ 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)
+{
+ sfi_mutex_init (&sfrepo->fluid_synth_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);
+ 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, 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 = 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,
+ gpointer items)
+{
+ 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_item_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,
+ BseItemSeq *items)
+{
+ gather_presets (BSE_ITEM (sfrepo), items);
+}
+
+fluid_synth_t *
+bse_sound_font_repo_lock_fluid_synth (BseSoundFontRepo *sfrepo)
+{
+ sfi_mutex_lock (&sfrepo->fluid_synth_mutex);
+ return sfrepo->fluid_synth;
+}
+
+void
+bse_sound_font_repo_unlock_fluid_synth (BseSoundFontRepo *sfrepo)
+{
+ sfi_mutex_unlock (&sfrepo->fluid_synth_mutex);
+}
+
+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 = 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;
+}
diff --git a/bse/bsesoundfontrepo.h b/bse/bsesoundfontrepo.h
new file mode 100644
index 0000000..312d624
--- /dev/null
+++ b/bse/bsesoundfontrepo.h
@@ -0,0 +1,94 @@
+/* BSE - Bedevilled Sound Engine
+ * Copyright (C) 1996-1999, 2000-2003 Tim Janik
+ * Copyright (C) 2009 Stefan Westerfeld
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#ifndef __BSE_SOUND_FONT_REPO_H__
+#define __BSE_SOUND_FONT_REPO_H__
+
+#include <bse/bsesuper.h>
+#include <fluidsynth.h>
+#include <bse/bsesoundfontosc.h>
+#include <bse/bseengine.h>
+
+
+G_BEGIN_DECLS
+
+
+/* --- object type macros --- */
+#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))
+
+
+/* --- BseSoundFontRepo object --- */
+#define BSE_FLUID_SYNTH_PROGRAM_SELECT -1
+typedef struct _BseFluidEvent BseFluidEvent;
+struct _BseFluidEvent
+{
+ guint64 tick_stamp;
+ int channel;
+ int command;
+ int arg1;
+ int arg2;
+ int sfont_id; /* required for program selection only */
+};
+struct _BseSoundFontRepo
+{
+ BseSuper parent_object;
+
+ BirnetMutex 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 parent_class;
+};
+
+
+/* --- prototypes --- */
+void bse_sound_font_repo_list_all_presets (BseSoundFontRepo *sfrepo,
+ BseItemSeq *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
+
+#endif /* __BSE_SOUND_FONT_REPO_H__ */
diff --git a/bse/bsesoundfontrepo.proc b/bse/bsesoundfontrepo.proc
new file mode 100644
index 0000000..8281837
--- /dev/null
+++ b/bse/bsesoundfontrepo.proc
@@ -0,0 +1,155 @@
+/* BSE - Bedevilled Sound Engine -*-mode: c;-*-
+ * Copyright (C) 2000-2003 Tim Janik
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * A copy of the GNU Lesser General Public License should ship along
+ * with this library; if not, see http://www.gnu.org/copyleft/.
+ */
+#include <bse/bseplugin.h>
+#include <bse/bseprocedure.h>
+#include <bse/bsesoundfontrepo.h>
+#include <bse/bsesoundfont.h>
+#include <bse/bseloader.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+/* --- auxlillary functions --- */
+static BseErrorType
+load_file (BseSoundFontRepo *sfrepo,
+ const gchar *file_name,
+ BseSoundFont **sound_font_p)
+{
+ gchar *fname = g_path_get_basename (file_name);
+ BseSoundFont *sound_font = g_object_new (BSE_TYPE_SOUND_FONT, "uname", fname, NULL);
+ g_free (fname);
+ bse_container_add_item (BSE_CONTAINER (sfrepo), BSE_ITEM (sound_font));
+
+ BseStorageBlob *blob = bse_storage_blob_new_from_file (file_name, FALSE);
+ BseErrorType 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;
+ }
+ else
+ {
+ bse_container_remove_item (BSE_CONTAINER (sfrepo), BSE_ITEM (sound_font));
+ *sound_font_p = NULL;
+ }
+ g_object_unref (sound_font);
+ return error;
+}
+
+/* --- procedures --- */
+AUTHORS = "Tim Janik <timj gtk org>";
+LICENSE = "GNU Lesser General Public License";
+
+
+METHOD (BseSoundFontRepo, load-file) {
+ HELP = "Load sound font from file";
+ IN = bse_param_spec_object ("sound_font_repo", "Sound Font Repo", NULL,
+ BSE_TYPE_SOUND_FONT_REPO, SFI_PARAM_STANDARD);
+ IN = sfi_pspec_string ("file_name", "File Name", "The file to import sound fonts from",
+ NULL, SFI_PARAM_STANDARD);
+ OUT = bse_param_spec_genum ("error", "Error", NULL,
+ BSE_TYPE_ERROR_TYPE, BSE_ERROR_NONE,
+ SFI_PARAM_STANDARD);
+}
+BODY (BseProcedureClass *proc,
+ const GValue *in_values,
+ GValue *out_values)
+{
+ /* extract parameter values */
+ BseSoundFontRepo *self = (BseSoundFontRepo*) bse_value_get_object (in_values++);
+ gchar *file_name = sfi_value_get_string (in_values++);
+ BseUndoStack *ustack;
+ BseErrorType error;
+ BseSoundFont *sound_font;
+
+ /* check parameters */
+ if (!BSE_IS_SOUND_FONT_REPO (self) || !file_name)
+ return BSE_ERROR_PROC_PARAM_INVAL;
+
+ 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 ...
+ */
+ error = BSE_ERROR_SOURCE_BUSY;
+ }
+ else
+ {
+ ustack = bse_item_undo_open (self, "load-sound-font");
+ error = load_file (self, file_name, &sound_font);
+ if (sound_font)
+ bse_item_push_undo_proc (self, "remove-sound-font", sound_font);
+ bse_item_undo_close (ustack);
+ }
+
+ /* set output parameters */
+ g_value_set_enum (out_values++, error);
+
+ return BSE_ERROR_NONE;
+}
+
+METHOD (BseSoundFontRepo, remove-sound-font) {
+ HELP = "Remove a sound font from repository";
+ IN = bse_param_spec_object ("sound_font_repo", "Sound Font Repo", NULL,
+ BSE_TYPE_SOUND_FONT_REPO, SFI_PARAM_STANDARD);
+ IN = bse_param_spec_object ("sound_font", "Sound Font", NULL,
+ BSE_TYPE_SOUND_FONT, SFI_PARAM_STANDARD);
+} BODY (BseProcedureClass *proc,
+ const GValue *in_values,
+ GValue *out_values)
+{
+ /* extract parameter values */
+ BseSoundFontRepo *self = (BseSoundFontRepo*) bse_value_get_object (in_values++);
+ BseItem *child = (BseItem*) bse_value_get_object (in_values++);
+ BseUndoStack *ustack;
+ BseErrorType error;
+
+ /* check parameters */
+ if (!BSE_IS_SOUND_FONT_REPO (self) || !BSE_IS_SOUND_FONT (child) ||
+ child->parent != BSE_ITEM (self))
+ return BSE_ERROR_PROC_PARAM_INVAL;
+
+ 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.
+ */
+ error = BSE_ERROR_SOURCE_BUSY;
+ }
+ else
+ {
+ /* action */
+ ustack = bse_item_undo_open (self, "remove-sound-font %s", bse_object_debug_name (child));
+ /* remove object references */
+ bse_container_uncross_undoable (BSE_CONTAINER (self), child);
+ /* how to get rid of the item once backed up */
+ bse_item_push_redo_proc (self, "remove-sound-font", child);
+ /* remove (without redo queueing) */
+ bse_container_remove_backedup (BSE_CONTAINER (self), child, ustack);
+ /* done */
+ bse_item_undo_close (ustack);
+
+ error = BSE_ERROR_NONE;
+ }
+
+ return error;
+}
diff --git a/bse/bsestorage.cc b/bse/bsestorage.cc
index 22438e2..e62eb4e 100644
--- a/bse/bsestorage.cc
+++ b/bse/bsestorage.cc
@@ -14,6 +14,8 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <sys/types.h>
+#include <signal.h>
using Bse::Flac1Handle;
@@ -41,7 +43,14 @@ struct _BseStorageItemLink
BseItem *to_item;
gchar *error;
};
-
+struct _BseStorageBlob
+{
+ SfiMutex mutex;
+ char *file_name;
+ int ref_count;
+ gboolean is_temp_file;
+ gulong id;
+};
/* --- prototypes --- */
static void bse_storage_init (BseStorage *self);
@@ -68,6 +77,8 @@ static GTokenType compat_parse_data_handle (BseStorage *self,
/* --- variables --- */
static gpointer parent_class = NULL;
static GQuark quark_raw_data_handle = 0;
+static GQuark quark_blob = 0;
+static GQuark quark_blob_id = 0;
static GQuark quark_vorbis_data_handle = 0;
static GQuark quark_flac_data_handle = 0;
static GQuark quark_dblock_data_handle = 0;
@@ -109,6 +120,10 @@ bse_storage_class_init (BseStorageClass *klass)
quark_flac_data_handle = g_quark_from_static_string ("flac-data-handle");
quark_dblock_data_handle = g_quark_from_static_string ("dblock-data-handle");
quark_bse_storage_binary_v0 = g_quark_from_static_string ("BseStorageBinaryV0");
+ quark_blob = g_quark_from_string ("blob");
+ quark_blob_id = g_quark_from_string ("blob-id");
+
+ bse_storage_blob_clean_files(); /* FIXME: maybe better placed in bsemain.c */
gobject_class->finalize = bse_storage_finalize;
}
@@ -129,6 +144,8 @@ bse_storage_init (BseStorage *self)
self->dblocks = NULL;
self->n_dblocks = 0;
self->free_me = NULL;
+ self->blobs = NULL;
+ self->n_blobs = 0;
bse_storage_reset (self);
}
@@ -149,9 +166,10 @@ bse_storage_turn_readable (BseStorage *self,
const gchar *storage_name)
{
BseStorageDBlock *dblocks;
+ BseStorageBlob **blobs;
const gchar *cmem;
gchar *text;
- guint n_dblocks, l;
+ guint n_dblocks, n_blobs, l;
g_return_if_fail (BSE_IS_STORAGE (self));
g_return_if_fail (BSE_STORAGE_DBLOCK_CONTAINED (self));
@@ -166,13 +184,19 @@ bse_storage_turn_readable (BseStorage *self,
text = (char*) g_memdup (cmem, l + 1);
dblocks = self->dblocks;
n_dblocks = self->n_dblocks;
+ blobs = self->blobs;
+ n_blobs = self->n_blobs;
self->dblocks = NULL;
self->n_dblocks = 0;
+ self->blobs = NULL;
+ self->n_blobs = 0;
bse_storage_input_text (self, text, storage_name);
self->free_me = text;
self->dblocks = dblocks;
self->n_dblocks = n_dblocks;
+ self->blobs = blobs;
+ self->n_blobs = n_blobs;
BSE_OBJECT_SET_FLAGS (self, BSE_STORAGE_DBLOCK_CONTAINED);
}
@@ -220,6 +244,12 @@ bse_storage_reset (BseStorage *self)
self->dblocks = NULL;
self->n_dblocks = 0;
+ for (i = 0; i < self->n_blobs; i++)
+ bse_storage_blob_unref (self->blobs[i]);
+ g_free (self->blobs);
+ self->blobs = NULL;
+ self->n_blobs = 0;
+
g_free (self->free_me);
self->free_me = NULL;
@@ -248,6 +278,16 @@ bse_storage_add_dblock (BseStorage *self,
return self->dblocks[i].id;
}
+static gulong
+bse_storage_add_blob (BseStorage *self,
+ BseStorageBlob *blob)
+{
+ guint i = self->n_blobs++;
+ self->blobs = g_renew (BseStorageBlob *, self->blobs, self->n_blobs);
+ self->blobs[i] = bse_storage_blob_ref (blob);
+ return self->blobs[i]->id;
+}
+
static BseStorageDBlock*
bse_storage_get_dblock (BseStorage *self,
gulong id)
@@ -564,21 +604,27 @@ storage_path_table_resolve_upath (BseStorage *self,
BseContainer *container,
gchar *upath)
{
- gchar *next_uname = strchr (upath, ':');
+ char *next_upath = strchr (upath, ':');
/* upaths consist of colon seperated unames from the item's ancestry */
- if (next_uname)
+ if (next_upath) /* A:B[:...] */
{
+ char *next_next_upath = strchr (next_upath + 1, ':');
BseItem *item;
- next_uname[0] = 0;
- item = storage_path_table_lookup (self, container, upath);
- next_uname[0] = ':';
+ next_upath[0] = 0;
+ if (next_next_upath)
+ next_next_upath[0] = 0;
+ /* lookup A */
+ item = storage_path_table_resolve_upath (self, container, upath);
+ next_upath[0] = ':';
+ if (next_next_upath)
+ next_next_upath[0] = ':';
+ /* lookup B[:...] in A */
if (BSE_IS_CONTAINER (item))
- return storage_path_table_lookup (self, BSE_CONTAINER (item), next_uname + 1);
+ return storage_path_table_resolve_upath (self, BSE_CONTAINER (item), next_upath + 1);
else
- return NULL;
+ return NULL;
}
- else
- return storage_path_table_lookup (self, container, upath);
+ return storage_path_table_lookup (self, container, upath);
}
static void
@@ -1566,6 +1612,310 @@ bse_storage_parse_data_handle_rest (BseStorage *self,
return parse_data_handle_trampoline (self, TRUE, data_handle_p, n_channels_p, mix_freq_p, osc_freq_p);
}
+/* blobs */
+
+BseStorageBlob *
+bse_storage_blob_ref (BseStorageBlob *blob)
+{
+ g_return_val_if_fail (blob != NULL, NULL);
+ g_return_val_if_fail (blob->ref_count > 0, NULL);
+
+ GSL_SPIN_LOCK (&blob->mutex);
+ blob->ref_count++;
+ GSL_SPIN_UNLOCK (&blob->mutex);
+
+ return blob;
+}
+
+const gchar *
+bse_storage_blob_file_name (BseStorageBlob *blob)
+{
+ g_return_val_if_fail (blob != NULL, NULL);
+ g_return_val_if_fail (blob->ref_count > 0, NULL);
+
+ GSL_SPIN_LOCK (&blob->mutex);
+ const gchar *file_name = blob->file_name;
+ GSL_SPIN_UNLOCK (&blob->mutex);
+
+ return file_name;
+}
+
+void
+bse_storage_blob_unref (BseStorageBlob *blob)
+{
+ g_return_if_fail (blob != NULL);
+ g_return_if_fail (blob->ref_count > 0);
+
+ GSL_SPIN_LOCK (&blob->mutex);
+ blob->ref_count--;
+ gboolean destroy = blob->ref_count == 0;
+ GSL_SPIN_UNLOCK (&blob->mutex);
+ if (destroy)
+ {
+ if (blob->is_temp_file)
+ {
+ unlink (blob->file_name);
+ /* FIXME: check error code and do what? */
+ }
+ sfi_mutex_destroy (&blob->mutex);
+ g_free (blob->file_name);
+ blob->file_name = NULL;
+ bse_id_free (blob->id);
+ g_free (blob);
+ }
+}
+
+/* search in /tmp for files called "bse-<user>-<pid>*"
+ * delete files if the pid does not exist any longer
+ */
+void
+bse_storage_blob_clean_files()
+{
+ GError *error;
+ const char *tmp_dir = g_get_tmp_dir();
+ GDir *dir = g_dir_open (tmp_dir, 0, &error);
+ if (dir)
+ {
+ char *pattern = g_strdup_printf ("bse-%s-", g_get_user_name());
+ const char *file_name;
+ while ((file_name = g_dir_read_name (dir)))
+ {
+ if (strncmp (pattern, file_name, strlen (pattern)) == 0)
+ {
+ int pid = atoi (file_name + strlen (pattern));
+
+ if (kill (pid, 0) == -1 && errno == ESRCH)
+ {
+ char *path = g_strdup_printf ("%s/%s", tmp_dir, file_name);
+ unlink (path);
+ g_free (path);
+ }
+ }
+ }
+ g_free (pattern);
+ g_dir_close (dir);
+ }
+}
+
+BseStorageBlob *
+bse_storage_blob_new_from_file (const char *file_name,
+ gboolean is_temp_file)
+{
+ BseStorageBlob *blob = g_new0 (BseStorageBlob, 1);
+ blob->file_name = g_strdup (file_name);
+ blob->ref_count = 1;
+ blob->is_temp_file = is_temp_file;
+ blob->id = bse_id_alloc();
+ sfi_mutex_init (&blob->mutex);
+ return blob;
+}
+
+gboolean
+bse_storage_blob_is_temp_file (BseStorageBlob *blob)
+{
+ g_return_val_if_fail (blob != NULL, FALSE);
+ g_return_val_if_fail (blob->ref_count > 0, FALSE);
+
+ GSL_SPIN_LOCK (&blob->mutex);
+ gboolean is_temp_file = blob->is_temp_file;
+ GSL_SPIN_UNLOCK (&blob->mutex);
+
+ return is_temp_file;
+}
+
+typedef struct
+{
+ BseStorageBlob *blob;
+ BseStorage *storage;
+ int fd;
+} WStoreBlob;
+
+static WStoreBlob *
+wstore_blob_new (BseStorage *storage,
+ BseStorageBlob *blob)
+{
+ WStoreBlob *wsb = (WStoreBlob *) g_new0 (WStoreBlob, 1);
+ wsb->blob = bse_storage_blob_ref (blob);
+ wsb->storage = storage;
+ wsb->fd = -1;
+ return wsb;
+}
+
+static gint /* -errno || length */
+wstore_blob_reader (gpointer data,
+ void *buffer,
+ guint blength)
+{
+ WStoreBlob *wsb = data;
+ if (wsb->fd == -1)
+ {
+ do
+ wsb->fd = open (bse_storage_blob_file_name (wsb->blob), O_RDONLY);
+ while (wsb->fd == -1 && errno == EINTR);
+ if (wsb->fd == -1)
+ {
+ bse_storage_error (wsb->storage, "file %s could not be opened: %s", bse_storage_blob_file_name
(wsb->blob), strerror (errno));
+ return -errno;
+ }
+ }
+ int n;
+ do
+ n = read (wsb->fd, buffer, blength);
+ while (n == -1 && errno == EINTR);
+ if (n < 0)
+ return -errno;
+ else
+ return n;
+}
+
+static void
+wstore_blob_destroy (gpointer data)
+{
+ WStoreBlob *wblob = data;
+ if (wblob->fd >= 0)
+ close (wblob->fd);
+ bse_storage_blob_unref (wblob->blob);
+}
+
+void
+bse_storage_put_blob (BseStorage *self,
+ BseStorageBlob *blob)
+{
+ if (BSE_STORAGE_DBLOCK_CONTAINED (self))
+ {
+ gulong id = bse_storage_add_blob (self, blob);
+ bse_storage_break (self);
+ bse_storage_printf (self, "(%s %lu)", g_quark_to_string (quark_blob_id), id);
+ }
+ else
+ {
+ bse_storage_break (self);
+ bse_storage_printf (self, "(%s ", g_quark_to_string (quark_blob));
+ bse_storage_push_level (self);
+ bse_storage_break (self);
+ sfi_wstore_put_binary (self->wstore, wstore_blob_reader, wstore_blob_new (self, blob),
wstore_blob_destroy);
+ bse_storage_pop_level (self);
+ bse_storage_putc (self, ')');
+ }
+}
+
+GTokenType
+bse_storage_parse_blob (BseStorage *self,
+ BseStorageBlob **blob)
+{
+ GScanner *scanner = bse_storage_get_scanner (self);
+ int bse_fd = -1;
+ int tmp_fd = -1;
+ char *file_name = g_strdup_printf ("%s/bse-%s-%u-%08x", g_get_tmp_dir(), g_get_user_name(), getpid(),
g_random_int());
+
+ *blob = NULL; /* on error, the resulting blob should be NULL */
+
+ parse_or_return (scanner, '(');
+ parse_or_return (scanner, G_TOKEN_IDENTIFIER);
+ if (g_quark_try_string (scanner->value.v_identifier) == quark_blob)
+ {
+ SfiNum offset, length;
+ GTokenType token = sfi_rstore_parse_binary (self->rstore, &offset, &length);
+ if (token != G_TOKEN_NONE)
+ return token;
+
+ char buffer[1024];
+ bse_fd = open (self->rstore->fname, O_RDONLY);
+ if (bse_fd < 0)
+ {
+ bse_storage_error (self, "couldn't open file %s for reading: %s\n", self->rstore->fname, strerror
(errno));
+ goto return_with_error;
+ }
+ tmp_fd = open (file_name, O_CREAT | O_WRONLY, 0600);
+ if (tmp_fd < 0)
+ {
+ bse_storage_error (self, "couldn't open file %s for writing: %s\n", file_name, strerror (errno));
+ goto return_with_error;
+ }
+ int result = lseek (bse_fd, offset, SEEK_SET);
+ if (result != offset)
+ {
+ bse_storage_error (self, "could not seek to position %lld in bse file %s\n", offset,
self->rstore->fname);
+ goto return_with_error;
+ }
+ int bytes_todo = length;
+ while (bytes_todo > 0)
+ {
+ int rbytes, wbytes;
+
+ do
+ rbytes = read (bse_fd, buffer, MIN (bytes_todo, 1024));
+ while (rbytes == -1 && errno == EINTR);
+
+ if (rbytes == -1)
+ {
+ bse_storage_error (self, "error while reading file %s: %s\n", self->rstore->fname, strerror
(errno));
+ goto return_with_error;
+ }
+ if (rbytes == 0)
+ {
+ bse_storage_error (self, "end-of-file occured too early in file %s\n", self->rstore->fname);
+ goto return_with_error;
+ }
+
+ int bytes_written = 0;
+ while (bytes_written != rbytes)
+ {
+ do
+ wbytes = write (tmp_fd, &buffer[bytes_written], rbytes - bytes_written);
+ while (wbytes == -1 && errno == EINTR);
+ if (wbytes == -1)
+ {
+ bse_storage_error (self, "error while writing file %s: %s\n", self->rstore->fname, strerror
(errno));
+ goto return_with_error;
+ }
+ bytes_written += wbytes;
+ }
+
+ bytes_todo -= rbytes;
+ }
+ close (bse_fd);
+ close (tmp_fd);
+ *blob = bse_storage_blob_new_from_file (file_name, TRUE);
+ g_free (file_name);
+ }
+ else if (g_quark_try_string (scanner->value.v_identifier) == quark_blob_id)
+ {
+ int i;
+ gulong id;
+ parse_or_return (scanner, G_TOKEN_INT);
+ id = scanner->value.v_int64;
+ *blob = NULL;
+ for (i = 0; i < self->n_blobs; i++)
+ {
+ if (self->blobs[i]->id == id)
+ *blob = bse_storage_blob_ref (self->blobs[i]);
+ }
+ if (!*blob)
+ {
+ g_warning ("failed to lookup storage blob with id=%ld\n", id);
+ goto return_with_error;
+ }
+ }
+ else
+ {
+ goto return_with_error;
+ }
+ parse_or_return (scanner, ')');
+
+ return G_TOKEN_NONE;
+
+return_with_error:
+ if (bse_fd != -1)
+ close (bse_fd);
+ if (tmp_fd != -1)
+ {
+ close (tmp_fd);
+ unlink (file_name);
+ }
+ return G_TOKEN_ERROR;
+}
+
BseErrorType
bse_storage_flush_fd (BseStorage *self,
gint fd)
diff --git a/bse/bsestorage.hh b/bse/bsestorage.hh
index 676fae0..c7f8645 100644
--- a/bse/bsestorage.hh
+++ b/bse/bsestorage.hh
@@ -41,6 +41,7 @@ typedef enum /*< skip >*/
/* --- BseStorage --- */
typedef struct _BseStorageDBlock BseStorageDBlock;
typedef struct _BseStorageItemLink BseStorageItemLink;
+typedef struct _BseStorageBlob BseStorageBlob;
typedef void (*BseStorageRestoreLink) (gpointer data,
BseStorage *storage,
BseItem *from_item,
@@ -62,6 +63,8 @@ struct BseStorage : BseObject {
/* internal data */
guint n_dblocks;
BseStorageDBlock *dblocks;
+ guint n_blobs;
+ BseStorageBlob **blobs;
gchar *free_me;
/* compat */ // VERSION-FIXME: needed only for <= 0.5.1
gfloat mix_freq;
@@ -118,6 +121,8 @@ void bse_storage_put_item_link (BseStorage *self,
void bse_storage_put_data_handle (BseStorage *self,
guint significant_bits,
GslDataHandle *dhandle);
+void bse_storage_put_blob (BseStorage *self,
+ BseStorageBlob *blob);
void bse_storage_put_xinfos (BseStorage *self,
gchar **xinfos);
BseErrorType bse_storage_flush_fd (BseStorage *self,
@@ -159,8 +164,19 @@ GTokenType bse_storage_parse_rest (BseStorage *self,
gpointer context_data,
BseTryStatement try_statement,
gpointer user_data);
+GTokenType bse_storage_parse_blob (BseStorage *self,
+ BseStorageBlob **blob);
gboolean bse_storage_check_parse_negate (BseStorage *self);
+/* --- bse storage blob --- */
+
+BseStorageBlob *bse_storage_blob_ref (BseStorageBlob *self);
+const gchar *bse_storage_blob_file_name (BseStorageBlob *self);
+gboolean bse_storage_blob_is_temp_file (BseStorageBlob *self);
+void bse_storage_blob_unref (BseStorageBlob *self);
+BseStorageBlob *bse_storage_blob_new_from_file (const gchar *file_name,
+ gboolean is_temp_file);
+void bse_storage_blob_clean_files (void);
/* --- short-hands --- */
#define bse_storage_get_scanner(s) ((s)->rstore->scanner)
diff --git a/bse/bsetrack.cc b/bse/bsetrack.cc
index 84677c6..b1fbba9 100644
--- a/bse/bsetrack.cc
+++ b/bse/bsetrack.cc
@@ -15,7 +15,11 @@
#include "bsemidivoice.hh"
#include "bsemidireceiver.hh"
#include "bsewaverepo.hh"
+#include "bsesoundfontrepo.h"
+#include "bsesoundfontpreset.h"
+#include "bsesoundfont.h"
#include "bsecxxplugin.hh"
+
#include <string.h>
#define XREF_DEBUG(...) BSE_KEY_DEBUG ("xref", __VA_ARGS__)
@@ -29,6 +33,7 @@ enum {
PROP_MUTED,
PROP_SNET,
PROP_WAVE,
+ PROP_SOUND_FONT_PRESET,
PROP_MIDI_CHANNEL,
PROP_N_VOICES,
PROP_PNET,
@@ -103,6 +108,9 @@ bse_track_init (BseTrack *self)
BSE_OBJECT_SET_FLAGS (self, BSE_SOURCE_FLAG_PRIVATE_INPUTS);
self->snet = NULL;
self->pnet = NULL;
+ self->sound_font_preset = NULL;
+ self->wave = NULL;
+ self->wnet = NULL;
self->max_voices = 16;
self->muted_SL = FALSE;
self->n_entries_SL = 0;
@@ -269,6 +277,12 @@ bse_track_get_candidates (BseItem *item,
bse_item_gather_items_typed (BSE_ITEM (wrepo), pc->items, BSE_TYPE_WAVE, BSE_TYPE_WAVE_REPO, FALSE);
}
break;
+ case PROP_SOUND_FONT_PRESET:
+ bse_property_candidate_relabel (pc, _("Sound Fonts"), _("List of available sound font presets to
choose as track instrument"));
+ project = bse_item_get_project (item);
+ if (project)
+ bse_sound_font_repo_list_all_presets (bse_project_get_sound_font_repo (project), pc->items);
+ break;
case PROP_SNET:
bse_property_candidate_relabel (pc, _("Available Synthesizers"), _("List of available synthesis
networks to choose a track instrument from"));
bse_item_gather_items_typed (item, pc->items, BSE_TYPE_CSYNTH, BSE_TYPE_PROJECT, FALSE);
@@ -327,6 +341,7 @@ set_amp_master_volume (BseSNet *snet, const char *amp_name, gchar **xinfos)
g_object_set (amp, "master-volume", volume, NULL);
}
}
+
static void
set_adsr_params (BseSNet *snet, const char *adsr_name, gchar **xinfos)
{
@@ -343,6 +358,15 @@ set_adsr_params (BseSNet *snet, const char *adsr_name, gchar **xinfos)
g_object_set (adsr, "attack_time", g_ascii_strtod (adsr_attack_time, NULL), NULL);
}
}
+
+static void
+track_uncross_sound_font_preset (BseItem *owner,
+ BseItem *ref_item)
+{
+ BseTrack *self = BSE_TRACK (owner);
+ bse_item_set (self, "sound-font-preset", NULL, NULL);
+}
+
static void
create_wnet (BseTrack *self,
BseWave *wave)
@@ -403,7 +427,30 @@ create_wnet (BseTrack *self,
}
static void
-clear_snet_and_wave (BseTrack *self)
+create_sound_font_net (BseTrack *self,
+ BseSoundFontPreset *preset)
+{
+ g_return_if_fail (self->sound_font_net == NULL);
+
+ self->sound_font_net = bse_project_create_intern_synth (bse_item_get_project (BSE_ITEM (self)),
+ "sound-font-snet",
+ BSE_TYPE_SNET);
+
+ bse_item_cross_link (BSE_ITEM (self), BSE_ITEM (self->sound_font_net), track_uncross_sound_font_preset);
+
+ if (self->sub_synth)
+ {
+ g_object_set (self->sub_synth, /* no undo */
+ "snet", self->sound_font_net,
+ NULL);
+ }
+
+ g_object_set (bse_container_resolve_upath (BSE_CONTAINER (self->sound_font_net), "sound-font-osc"), /* no
undo */
+ "preset", preset,
+ NULL);
+}
+static void
+clear_snet_and_wave_and_sfpreset (BseTrack *self)
{
g_return_if_fail (!self->sub_synth || !BSE_SOURCE_PREPARED (self->sub_synth));
@@ -432,6 +479,20 @@ clear_snet_and_wave (BseTrack *self)
self->wnet = NULL;
bse_container_remove_item (BSE_CONTAINER (bse_item_get_project (BSE_ITEM (self))), BSE_ITEM (wnet));
}
+ if (self->sound_font_preset)
+ {
+ bse_object_unproxy_notifies (self->sound_font_preset, self, "changed");
+ bse_item_cross_unlink (BSE_ITEM (self), BSE_ITEM (self->sound_font_preset),
track_uncross_sound_font_preset);
+ self->sound_font_preset = NULL;
+ g_object_notify (self, "sound_font_preset");
+ }
+ if (self->sound_font_net)
+ {
+ BseSNet *sound_font_net = self->sound_font_net;
+ bse_item_cross_unlink (BSE_ITEM (self), BSE_ITEM (self->sound_font_net),
track_uncross_sound_font_preset);
+ self->sound_font_net = NULL;
+ bse_container_remove_item (BSE_CONTAINER (bse_item_get_project (BSE_ITEM (self))), BSE_ITEM
(sound_font_net));
+ }
}
static void
@@ -456,7 +517,7 @@ bse_track_set_property (GObject *object,
BseSNet *snet = (BseSNet*) bse_value_get_object (value);
if (snet || self->snet)
{
- clear_snet_and_wave (self);
+ clear_snet_and_wave_and_sfpreset (self);
self->snet = snet;
if (self->snet)
{
@@ -476,7 +537,7 @@ bse_track_set_property (GObject *object,
BseWave *wave = (BseWave*) bse_value_get_object (value);
if (wave || self->wave)
{
- clear_snet_and_wave (self);
+ clear_snet_and_wave_and_sfpreset (self);
self->wave = wave;
if (self->wave)
@@ -488,6 +549,24 @@ bse_track_set_property (GObject *object,
}
}
break;
+ case PROP_SOUND_FONT_PRESET:
+ if (!self->sub_synth || !BSE_SOURCE_PREPARED (self->sub_synth))
+ {
+ BseSoundFontPreset *sound_font_preset = bse_value_get_object (value);
+ if (sound_font_preset || self->sound_font_preset)
+ {
+ clear_snet_and_wave_and_sfpreset (self);
+
+ self->sound_font_preset = sound_font_preset;
+ if (self->sound_font_preset)
+ {
+ create_sound_font_net (self, sound_font_preset);
+ bse_item_cross_link (BSE_ITEM (self), BSE_ITEM (self->sound_font_preset),
track_uncross_sound_font_preset);
+ bse_object_proxy_notifies (self->sound_font_preset, self, "changed");
+ }
+ }
+ }
+ break;
case PROP_N_VOICES:
if (!self->postprocess || !BSE_SOURCE_PREPARED (self->postprocess))
self->max_voices = sfi_value_get_int (value);
@@ -560,6 +639,9 @@ bse_track_get_property (GObject *object,
case PROP_WAVE:
bse_value_set_object (value, self->wave);
break;
+ case PROP_SOUND_FONT_PRESET:
+ bse_value_set_object (value, self->sound_font_preset);
+ break;
case PROP_N_VOICES:
sfi_value_set_int (value, self->max_voices);
break;
@@ -850,11 +932,11 @@ static void
bse_track_update_midi_channel (BseTrack *self)
{
if (self->voice_switch)
- {
- bse_sub_synth_set_midi_channel (BSE_SUB_SYNTH (self->sub_synth), self->midi_channel_SL);
- bse_sub_synth_set_midi_channel (BSE_SUB_SYNTH (self->postprocess), self->midi_channel_SL);
- bse_midi_voice_switch_set_midi_channel (BSE_MIDI_VOICE_SWITCH (self->voice_switch),
self->midi_channel_SL);
- }
+ {
+ bse_sub_synth_set_midi_channel (BSE_SUB_SYNTH (self->sub_synth), self->midi_channel_SL);
+ bse_sub_synth_set_midi_channel (BSE_SUB_SYNTH (self->postprocess), self->midi_channel_SL);
+ bse_midi_voice_switch_set_midi_channel (BSE_MIDI_VOICE_SWITCH (self->voice_switch),
self->midi_channel_SL);
+ }
}
static void
@@ -885,7 +967,7 @@ bse_track_context_dismiss (BseSource *source,
void
bse_track_remove_modules (BseTrack *self,
- BseContainer *container)
+ BseContainer *container)
{
g_return_if_fail (BSE_IS_TRACK (self));
g_return_if_fail (BSE_IS_CONTAINER (container));
@@ -903,19 +985,21 @@ bse_track_remove_modules (BseTrack *self,
void
bse_track_clone_voices (BseTrack *self,
- BseSNet *snet,
- guint context,
+ BseSNet *snet,
+ guint context,
BseMidiContext mcontext,
- BseTrans *trans)
+ BseTrans *trans)
{
guint i;
g_return_if_fail (BSE_IS_TRACK (self));
g_return_if_fail (BSE_IS_SNET (snet));
g_return_if_fail (trans != NULL);
-
- for (i = 0; i < self->max_voices - 1; i++)
- bse_snet_context_clone_branch (snet, context, BSE_SOURCE (self), mcontext, trans);
+ if (!self->sound_font_preset)
+ {
+ for (i = 0; i < self->max_voices - 1; i++)
+ bse_snet_context_clone_branch (snet, context, BSE_SOURCE (self), mcontext, trans);
+ }
}
static void
@@ -1035,6 +1119,12 @@ bse_track_class_init (BseTrackClass *klass)
BSE_TYPE_WAVE,
SFI_PARAM_STANDARD ":unprepared"));
bse_object_class_add_param (object_class, _("Synth Input"),
+ PROP_SOUND_FONT_PRESET,
+ bse_param_spec_object (("sound_font_preset"), _("Sound Font Preset"),
+ _("Sound font preset to be used as instrument"),
+ BSE_TYPE_SOUND_FONT_PRESET,
+ SFI_PARAM_STANDARD ":unprepared"));
+ bse_object_class_add_param (object_class, _("Synth Input"),
PROP_N_VOICES,
sfi_pspec_int ("n_voices", _("Max Voices"), _("Maximum number of voices for
simultaneous playback"),
16, 1, 256, 1,
diff --git a/bse/bsetrack.hh b/bse/bsetrack.hh
index 83a06a7..deb50df 100644
--- a/bse/bsetrack.hh
+++ b/bse/bsetrack.hh
@@ -29,6 +29,11 @@ struct BseTrack : BseContextMerger {
/* wave synthesis */
BseWave *wave;
BseSNet *wnet;
+
+ /* sound font synthesis */
+ BseSoundFontPreset *sound_font_preset;
+ BseSNet *sound_font_net;
+
/* playback intergration */
BseSource *sub_synth;
BseSource *voice_input;
diff --git a/bse/zintern/Makefile.am b/bse/zintern/Makefile.am
index 7cd7251..9bbe5c9 100644
--- a/bse/zintern/Makefile.am
+++ b/bse/zintern/Makefile.am
@@ -11,6 +11,7 @@ ZFILE_DEFS = $(strip \
adsr-wave-2 $(srcdir)/adsr-wave-2.bse \
plain-wave-1 $(srcdir)/plain-wave-1.bse \
plain-wave-2 $(srcdir)/plain-wave-2.bse \
+ sound-font-snet $(srcdir)/sound-font-snet.bse \
)
gen_sources = xgen-bzc xgen-bzh
diff --git a/bse/zintern/sound-font-snet.bse b/bse/zintern/sound-font-snet.bse
new file mode 100644
index 0000000..38c981e
--- /dev/null
+++ b/bse/zintern/sound-font-snet.bse
@@ -0,0 +1,17 @@
+; BseProject
+
+(bse-version "0.7.2")
+
+(container-child "BseCSynth::%bse-intern-sound-font-snet"
+ (modification-time "2009-03-14 14:23:28")
+ (creation-time "2003-04-27 20:45:24")
+ (license "Provided \"as is\", WITHOUT ANY WARRANTY (http://beast.gtk.org/LICENSE-AS-IS)")
+ (author "Tim Janik, Stefan Westerfeld")
+ (container-child "BseInstrumentOutput::instrument-output"
+ (pos-y 1)
+ (pos-x 3)
+ (source-input "left-audio" (link 1 "sound-font-osc") "left-out")
+ (source-input "right-audio" (link 1 "sound-font-osc") "right-out")
+ (source-input "synth-done" (link 1 "sound-font-osc") "done-out"))
+ (container-child "BseSoundFontOsc::sound-font-osc"
+ (pos-y 1)))
diff --git a/configure.ac b/configure.ac
index a7d9f28..1ae3882 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,9 +367,14 @@ AC_DEFUN([AC_BSE_REQUIREMENTS],
AC_MSG_ERROR([Ogg/Vorbis is missing, but required])
fi
+ dnl Check for FluidSynth
+ PKG_CHECK_MODULES(FLUID, fluidsynth >= 1.0.6)
+
dnl # --- complete CFLAGS/LIBS setup ---
BSE_CFLAGS="$MAD_CFLAGS $FLAC_CFLAGS $SFI_CPPFLAGS"
BSE_LIBS="$OV_LIBS $MAD_LIBS $FLAC_LIBS $SFI_LIBS"
+ BSE_CFLAGS="$FLUID_CFLAGS $BSE_CFLAGS"
+ BSE_LIBS="$FLUID_LIBS $BSE_LIBS"
AC_SUBST(BSE_CFLAGS)
AC_SUBST(BSE_LIBS)
dnl # --- figure stuff for bse.pc ---
diff --git a/po/POTSCAN b/po/POTSCAN
index aadb347..8a75ee0 100644
--- a/po/POTSCAN
+++ b/po/POTSCAN
@@ -43,6 +43,7 @@ beast-gtk/bstrecords.idl
beast-gtk/bstsampleeditor.cc
beast-gtk/bstscrollgraph.cc
beast-gtk/bstsnetrouter.cc
+beast-gtk/bstsoundfontview.c
beast-gtk/bstsupershell.cc
beast-gtk/bsttrackrollctrl.cc
beast-gtk/bsttracksynthdialog.cc
@@ -91,6 +92,7 @@ bse/bseserver.cc
bse/bsesnooper.cc
bse/bsesong.cc
bse/bsesong.proc
+bse/bsesoundfontosc.c
bse/bsesource.proc
bse/bsestandardosc.cc
bse/bsesubiport.cc
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]