>From 1aaabe73c1cabce0f478c2124e886812fd7b6e5f Mon Sep 17 00:00:00 2001 From: Tim Janik Date: Wed, 27 Feb 2013 01:38:58 +0100 Subject: [PATCH] Merge and squash branch 'soundfont-support' 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 | 6 +- beast-gtk/bstfiledialog.c | 47 +++ beast-gtk/bstfiledialog.h | 35 +- beast-gtk/bstsoundfontpresetview.c | 47 +++ beast-gtk/bstsoundfontpresetview.h | 52 +++ beast-gtk/bstsoundfontview.c | 155 +++++++++ beast-gtk/bstsoundfontview.h | 55 ++++ beast-gtk/bstsupershell.c | 17 + beast-gtk/bsttracksynthdialog.c | 34 +- beast-gtk/bsttracksynthdialog.h | 8 +- beast-gtk/bsttrackview.c | 32 +- beast-gtk/bstutils.c | 2 + beast-gtk/dialogs/radgets-beast.xml | 19 ++ bse/Makefile.am | 7 +- bse/bsedefs.h | 6 + bse/bsemidireceiver.cc | 271 +++++++++++---- bse/bsemidireceiver.h | 14 + bse/bseproject.c | 24 ++ bse/bseproject.h | 1 + bse/bseproject.proc | 29 ++ 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.c | 373 ++++++++++++++++++++- bse/bsestorage.h | 17 +- bse/bsetrack.c | 112 ++++++- bse/bsetrack.h | 4 + bse/zintern/Makefile.am | 1 + bse/zintern/sound-font-snet.bse | 17 + configure.in | 7 +- po/POTSCAN | 2 + 37 files changed, 3300 insertions(+), 133 deletions(-) create mode 100644 beast-gtk/bstsoundfontpresetview.c create mode 100644 beast-gtk/bstsoundfontpresetview.h create mode 100644 beast-gtk/bstsoundfontview.c create mode 100644 beast-gtk/bstsoundfontview.h create mode 100644 bse/bsesoundfont.c create mode 100644 bse/bsesoundfont.h create mode 100644 bse/bsesoundfontosc.c create mode 100644 bse/bsesoundfontosc.h create mode 100644 bse/bsesoundfontpreset.c create mode 100644 bse/bsesoundfontpreset.h create mode 100644 bse/bsesoundfontrepo.c create mode 100644 bse/bsesoundfontrepo.h create mode 100644 bse/bsesoundfontrepo.proc create mode 100644 bse/zintern/sound-font-snet.bse diff --git a/beast-gtk/Makefile.am b/beast-gtk/Makefile.am index 1f6fbaa..22652b9 100644 --- a/beast-gtk/Makefile.am +++ b/beast-gtk/Makefile.am @@ -38,8 +38,8 @@ beast_headers = $(strip \ bstsnetrouter.h bstsplash.h bsttrackview.h bstsupershell.h \ bstusermessage.h bstdial.h bsttracksynthdialog.h bstwaveeditor.h \ bstzoomedwindow.h bstskinconfig.h bstmsgabsorb.h bstsampleeditor.h \ - bstrackview.h bsttreestores.h bstbseutils.h bstutils.h \ - bstdefs.h \ + bstrackview.h bsttreestores.h bstbseutils.h \ + bstutils.h bstdefs.h bstsoundfontview.h bstsoundfontpresetview.h \ ) EXTRA_DIST += $(beast_headers) # BEAST sources to build the program from @@ -61,7 +61,7 @@ beast_sources = $(strip \ bstusermessage.c bstdial.c bsttracksynthdialog.c bstwaveeditor.c \ bstzoomedwindow.c bstskinconfig.c bstmsgabsorb.c bstsampleeditor.c \ bstrackview.c bsttreestores.c bstbseutils.c bstutils.c \ - $(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.c b/beast-gtk/bstfiledialog.c index e4e7062..4991a2c 100644 --- a/beast-gtk/bstfiledialog.c +++ b/beast-gtk/bstfiledialog.c @@ -244,6 +244,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; @@ -333,6 +343,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)); @@ -341,6 +352,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)); @@ -783,6 +795,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 = g_object_new (BST_TYPE_FILE_DIALOG, NULL); @@ -920,6 +963,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; } if (swin) gxk_status_window_pop (); diff --git a/beast-gtk/bstfiledialog.h b/beast-gtk/bstfiledialog.h index 1705023..54e1def 100644 --- a/beast-gtk/bstfiledialog.h +++ b/beast-gtk/bstfiledialog.h @@ -39,21 +39,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 { @@ -109,6 +111,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.c b/beast-gtk/bstsupershell.c index 2781b0c..3a867db 100644 --- a/beast-gtk/bstsupershell.c +++ b/beast-gtk/bstsupershell.c @@ -21,6 +21,7 @@ #include "bstbusmixer.h" #include "bstbusview.h" #include "bstwaveview.h" +#include "bstsoundfontview.h" #include "bstrackview.h" #include "bstsnetrouter.h" #include "bstgconfig.h" @@ -235,6 +236,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) { @@ -259,6 +274,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.c b/beast-gtk/bsttracksynthdialog.c index 3c8414e..766abdb 100644 --- a/beast-gtk/bsttracksynthdialog.c +++ b/beast-gtk/bsttracksynthdialog.c @@ -57,7 +57,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 = g_object_new (GXK_TYPE_NOTEBOOK, @@ -120,6 +120,10 @@ bst_track_synth_dialog_init (BstTrackSynthDialog *self) self->wpage = 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, bst_track_synth_dialog_activate, self); @@ -236,6 +240,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) @@ -246,6 +253,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); @@ -253,8 +262,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"), @@ -271,14 +282,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 @@ -303,9 +317,15 @@ 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; /* notify and done */ diff --git a/beast-gtk/bsttracksynthdialog.h b/beast-gtk/bsttracksynthdialog.h index f5456b1..36afb4e 100644 --- a/beast-gtk/bsttracksynthdialog.h +++ b/beast-gtk/bsttracksynthdialog.h @@ -19,6 +19,7 @@ #include "bstutils.h" #include "bstwaveview.h" +#include "bstsoundfontview.h" G_BEGIN_DECLS @@ -44,6 +45,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; @@ -69,12 +71,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); diff --git a/beast-gtk/bsttrackview.c b/beast-gtk/bsttrackview.c index 2c1564b..ed5fdc2 100644 --- a/beast-gtk/bsttrackview.c +++ b/beast-gtk/bsttrackview.c @@ -135,7 +135,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: @@ -154,8 +154,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); @@ -190,7 +200,7 @@ track_view_synth_edited (BstTrackView *self, const gchar *text) { g_return_if_fail (BST_IS_TRACK_VIEW (self)); - + if (strpath) { gint row = gxk_tree_spath_index0 (strpath); @@ -199,11 +209,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); @@ -211,11 +223,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); } } @@ -296,6 +310,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); } @@ -323,6 +340,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.c b/beast-gtk/bstutils.c index f2d5dc1..e2b369d 100644 --- a/beast-gtk/bstutils.c +++ b/beast-gtk/bstutils.c @@ -20,6 +20,7 @@ #include "bstmenus.h" #include "bsttrackview.h" #include "bstwaveview.h" +#include "bstsoundfontview.h" #include "bstpartview.h" #include "bstbusmixer.h" #include "bstbuseditor.h" @@ -117,6 +118,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 @@ + + + + + + + + + + + + + + + + + + + diff --git a/bse/Makefile.am b/bse/Makefile.am index b3fe174..28dd31f 100644 --- a/bse/Makefile.am +++ b/bse/Makefile.am @@ -56,7 +56,8 @@ bse_public_headers = $(strip \ bsenote.h bsemidifile.h bseblockutils.hh \ bsecxxvalue.hh bsecxxutils.hh bsecxxbase.hh bsecxxclosure.hh \ bsecxxarg.hh bsecxxmodule.hh bsecxxplugin.hh bseloader.h \ - bseresampler.hh bseresamplerimpl.hh \ + bseresampler.hh bseresamplerimpl.hh bsesoundfontrepo.h bsesoundfont.h \ + bsesoundfontpreset.h bsesoundfontosc.h \ ) # BSE C & C++ sources bse_sources = $(strip \ @@ -64,7 +65,7 @@ bse_sources = $(strip \ gslfilter.c gslcommon.c \ gsldatahandle-vorbis.c gslvorbis-enc.c gsldatacache.c gslvorbis-cutter.c \ gsldatahandle-mad.c gslfilehash.c gsldatautils.c \ - gslwaveosc.c gslosctable.c gslmagic.c \ + gslwaveosc.c gslosctable.c gslmagic.c \ \ bseengine.c bseenginemaster.c bseengineschedule.c bseengineutils.c \ bsebus.cc bsecategories.cc \ @@ -95,6 +96,7 @@ bse_sources = $(strip \ bsebusmodule.cc \ bsecore.cc \ bseprobe.cc \ + bsesoundfontrepo.c bsesoundfont.c bsesoundfontpreset.c bsesoundfontosc.c \ ) # BSE Synthesis Modules bse_idl_sources = @@ -117,6 +119,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.c) # non-compile and non-install sources required diff --git a/bse/bsedefs.h b/bse/bsedefs.h index 9339cc1..26c08a0 100644 --- a/bse/bsedefs.h +++ b/bse/bsedefs.h @@ -84,6 +84,12 @@ typedef struct _BseSNetClass BseSNetClass; typedef struct _BseSong BseSong; typedef struct _BseSongClass 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; typedef struct _BseSource BseSource; typedef struct _BseSourceClass BseSourceClass; typedef struct _BseStorage BseStorage; diff --git a/bse/bsemidireceiver.cc b/bse/bsemidireceiver.cc index 67394e2..deb7ee4 100644 --- a/bse/bsemidireceiver.cc +++ b/bse/bsemidireceiver.cc @@ -26,6 +26,7 @@ #include #include #include +#include namespace { using namespace Bse; @@ -231,6 +232,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; @@ -246,6 +272,7 @@ struct MidiChannel { guint n_voices; VoiceSwitch **voices; VoiceInputTable voice_input_table; + std::list event_handlers; MidiChannel (guint mc) : midi_channel (mc), poly_enabled (0) @@ -265,6 +292,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::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) @@ -444,6 +486,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)); + } }; @@ -1043,6 +1103,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::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, @@ -1530,6 +1619,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) { @@ -1980,79 +2103,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); - DEBUG_EVENTS ("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); - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("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: - DEBUG_EVENTS ("MidiChannel[%u]: Ignoring Event %u (stamp:%llu)", event->channel, - event->status, event->delta_time); - break; + switch (event->status) + { + case BSE_MIDI_NOTE_ON: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("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: + DEBUG_EVENTS ("MidiChannel[%u]: Ignoring Event %u (stamp:%llu)", event->channel, + event->status, event->delta_time); + break; + } } if (self->notifier) { diff --git a/bse/bsemidireceiver.h b/bse/bsemidireceiver.h index 4c60955..dd11a58 100644 --- a/bse/bsemidireceiver.h +++ b/bse/bsemidireceiver.h @@ -35,6 +35,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); @@ -68,6 +72,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.c b/bse/bseproject.c index 340e0e2..fcd8ef9 100644 --- a/bse/bseproject.c +++ b/bse/bseproject.c @@ -31,6 +31,7 @@ #include "bsemidinotifier.h" #include "gslcommon.h" #include "bseengine.h" +#include "bsesoundfontrepo.h" #include #include #include @@ -186,6 +187,7 @@ bse_project_init (BseProject *self, gpointer rclass) { BseWaveRepo *wrepo; + BseSoundFontRepo *sfrepo; self->state = BSE_PROJECT_INACTIVE; self->supers = NULL; @@ -202,8 +204,12 @@ bse_project_init (BseProject *self, wrepo = bse_container_new_child (BSE_CONTAINER (self), BSE_TYPE_WAVE_REPO, "uname", "Wave-Repository", NULL); + 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 @@ -438,6 +444,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); @@ -657,6 +670,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.h b/bse/bseproject.h index a4aa0c4..cb82e81 100644 --- a/bse/bseproject.h +++ b/bse/bseproject.h @@ -90,6 +90,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); gpointer bse_project_create_intern_synth (BseProject *project, const gchar *synth_name, diff --git a/bse/bseproject.proc b/bse/bseproject.proc index 52b7848..54b2a7f 100644 --- a/bse/bseproject.proc +++ b/bse/bseproject.proc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -300,6 +301,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 + +#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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include "gslcommon.h" + +#include + +/* --- 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 +#include + +#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 +#include + +#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 +#include + +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 +#include +#include +#include + + +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 +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* --- 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 "; +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.c b/bse/bsestorage.c index f206984..3c7f247 100644 --- a/bse/bsestorage.c +++ b/bse/bsestorage.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include /* --- macros --- */ @@ -55,7 +57,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); @@ -78,10 +87,11 @@ static GTokenType compat_parse_data_handle (BseStorage *self, gfloat *mix_freq_p, gfloat *osc_freq_p); - /* --- 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_dblock_data_handle = 0; static GQuark quark_bse_storage_binary_v0 = 0; @@ -121,6 +131,10 @@ bse_storage_class_init (BseStorageClass *class) quark_vorbis_data_handle = g_quark_from_static_string ("vorbis-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; } @@ -141,6 +155,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); } @@ -161,9 +177,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)); @@ -178,13 +195,19 @@ bse_storage_turn_readable (BseStorage *self, text = 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); } @@ -232,6 +255,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; @@ -260,6 +289,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) @@ -576,21 +615,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 @@ -1718,6 +1763,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--*" + * 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.h b/bse/bsestorage.h index 0d5eb7c..b567f32 100644 --- a/bse/bsestorage.h +++ b/bse/bsestorage.h @@ -56,6 +56,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, @@ -79,6 +80,8 @@ struct _BseStorage /* 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; @@ -141,6 +144,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, @@ -185,8 +190,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) @@ -197,7 +213,6 @@ gboolean bse_storage_check_parse_negate (BseStorage *self); #define bse_storage_putc(s,c) sfi_wstore_putc ((s)->wstore, c) #define bse_storage_puts(s,b) sfi_wstore_puts ((s)->wstore, b) - G_END_DECLS #endif /* __BSE_STORAGE_H__ */ diff --git a/bse/bsetrack.c b/bse/bsetrack.c index 6e4cf7b..533cac3 100644 --- a/bse/bsetrack.c +++ b/bse/bsetrack.c @@ -30,6 +30,9 @@ #include "bsemidivoice.h" #include "bsemidireceiver.h" #include "bsewaverepo.h" +#include "bsesoundfontrepo.h" +#include "bsesoundfontpreset.h" +#include "bsesoundfont.h" #include static SFI_MSG_TYPE_DEFINE (debug_xref, "xref", SFI_MSG_DEBUG, NULL); @@ -45,6 +48,7 @@ enum { PROP_MUTED, PROP_SNET, PROP_WAVE, + PROP_SOUND_FONT_PRESET, PROP_MIDI_CHANNEL, PROP_N_VOICES, PROP_PNET, @@ -119,6 +123,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; @@ -288,6 +295,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); @@ -334,6 +347,14 @@ track_uncross_wave (BseItem *owner, } 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) { @@ -384,11 +405,34 @@ create_wnet (BseTrack *self, g_warning ("track: waves with the play-type \"%s\" are not supported by this version of beast\n", synthesis_network); } +} + +static void +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 (BseTrack *self) +clear_snet_and_wave_and_sfpreset (BseTrack *self) { g_return_if_fail (!self->sub_synth || !BSE_SOURCE_PREPARED (self->sub_synth)); @@ -417,6 +461,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 @@ -441,7 +499,7 @@ bse_track_set_property (GObject *object, BseSNet *snet = 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) { @@ -461,7 +519,7 @@ bse_track_set_property (GObject *object, BseWave *wave = 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) @@ -473,6 +531,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); @@ -545,6 +621,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; @@ -736,7 +815,7 @@ bse_track_add_modules (BseTrack *self, /* midi voice input */ self->voice_input = bse_container_new_child (container, BSE_TYPE_MIDI_VOICE_INPUT, NULL); bse_item_set_internal (self->voice_input, TRUE); - + /* sub synth */ self->sub_synth = bse_container_new_child_bname (container, BSE_TYPE_SUB_SYNTH, "Track-Instrument", "in_port_1", "frequency", @@ -750,7 +829,7 @@ bse_track_add_modules (BseTrack *self, "snet", self->snet, NULL); bse_item_set_internal (self->sub_synth, TRUE); - + /* voice input <-> sub-synth */ bse_source_must_set_input (self->sub_synth, 0, self->voice_input, BSE_MIDI_VOICE_INPUT_OCHANNEL_FREQUENCY); @@ -760,7 +839,7 @@ bse_track_add_modules (BseTrack *self, self->voice_input, BSE_MIDI_VOICE_INPUT_OCHANNEL_VELOCITY); bse_source_must_set_input (self->sub_synth, 3, self->voice_input, BSE_MIDI_VOICE_INPUT_OCHANNEL_AFTERTOUCH); - + /* midi voice switch */ self->voice_switch = bse_container_new_child (container, BSE_TYPE_MIDI_VOICE_SWITCH, NULL); bse_item_set_internal (self->voice_switch, TRUE); @@ -773,18 +852,18 @@ bse_track_add_modules (BseTrack *self, self->sub_synth, 1); bse_source_must_set_input (self->voice_switch, BSE_MIDI_VOICE_SWITCH_ICHANNEL_DISCONNECT, self->sub_synth, 3); - + /* midi voice switch <-> context merger */ bse_source_must_set_input (BSE_SOURCE (self), 0, self->voice_switch, BSE_MIDI_VOICE_SWITCH_OCHANNEL_LEFT); bse_source_must_set_input (BSE_SOURCE (self), 1, self->voice_switch, BSE_MIDI_VOICE_SWITCH_OCHANNEL_RIGHT); - + /* postprocess */ self->postprocess = bse_container_new_child_bname (container, BSE_TYPE_SUB_SYNTH, "Track-Postprocess", NULL); bse_item_set_internal (self->postprocess, TRUE); bse_sub_synth_set_null_shortcut (BSE_SUB_SYNTH (self->postprocess), TRUE); - + /* context merger <-> postprocess */ bse_source_must_set_input (self->postprocess, 0, BSE_SOURCE (self), 0); bse_source_must_set_input (self->postprocess, 1, BSE_SOURCE (self), 1); @@ -827,7 +906,7 @@ bse_track_get_last_tick (BseTrack *self) } else last_tick += 1; /* always return one after */ - + return last_tick; } @@ -899,8 +978,11 @@ bse_track_clone_voices (BseTrack *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 @@ -1020,6 +1102,12 @@ bse_track_class_init (BseTrackClass *class) 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.h b/bse/bsetrack.h index c215b2a..91f5e16 100644 --- a/bse/bsetrack.h +++ b/bse/bsetrack.h @@ -51,6 +51,10 @@ struct _BseTrack 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 cf05464..66824de 100644 --- a/bse/zintern/Makefile.am +++ b/bse/zintern/Makefile.am @@ -12,6 +12,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.in b/configure.in index 7e73ae8..fcfbfb2 100644 --- a/configure.in +++ b/configure.in @@ -265,9 +265,12 @@ 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 $SFI_CFLAGS" - BSE_LIBS="$OV_LIBS $MAD_LIBS $SFI_LIBS" + BSE_CFLAGS="$MAD_CFLAGS $SFI_CFLAGS $FLUID_CFLAGS" + BSE_LIBS="$OV_LIBS $MAD_LIBS $SFI_LIBS $FLUID_LIBS" AC_SUBST(BSE_CFLAGS) AC_SUBST(BSE_LIBS) dnl # --- figure stuff for bse.pc --- diff --git a/po/POTSCAN b/po/POTSCAN index 645e929..f20f4a8 100644 --- a/po/POTSCAN +++ b/po/POTSCAN @@ -43,6 +43,7 @@ beast-gtk/bstrecords.idl beast-gtk/bstsampleeditor.c beast-gtk/bstscrollgraph.c beast-gtk/bstsnetrouter.c +beast-gtk/bstsoundfontview.c beast-gtk/bstsupershell.c beast-gtk/bsttrackrollctrl.c beast-gtk/bsttracksynthdialog.c @@ -93,6 +94,7 @@ bse/bseserver.c bse/bsesnooper.c bse/bsesong.c bse/bsesong.proc +bse/bsesoundfontosc.c bse/bsesource.proc bse/bsestandardosc.c bse/bsesubiport.c -- 1.7.9.5