Merge branch 'master' into balance

This commit is contained in:
Tom M 2018-02-11 15:24:50 +01:00 committed by GitHub
commit d3cfa28e5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 881 additions and 433 deletions

View File

@ -6,10 +6,9 @@
| Linux/MacOSX| n.a. | [![Build Status Travis](https://travis-ci.org/FluidSynth/fluidsynth.svg?branch=master)](https://travis-ci.org/FluidSynth/fluidsynth) |
| Windows | [![Build status Appveyor](https://ci.appveyor.com/api/projects/status/n24ybk0dmttjwdk2/branch/master?svg=true)](https://ci.appveyor.com/project/derselbst/fluidsynth) | [![Build status](https://ci.appveyor.com/api/projects/status/anbmtebt5uk4q1it/branch/master?svg=true)](https://ci.appveyor.com/project/derselbst/fluidsynth-g2ouw) |
### FluidSynth is a software real-time synthesizer based on the Soundfont 2 specifications.
FluidSynth is a software real-time synthesizer based on the
Soundfont 2 specifications.
[![OHLOH Project Stats](https://www.openhub.net/p/fluidsynth/widgets/project_thin_badge?format=gif)](https://www.openhub.net/p/fluidsynth)
FluidSynth reads and handles MIDI events from the MIDI input
device. It is the software analogue of a MIDI synthesizer. FluidSynth

4
TODO
View File

@ -55,10 +55,8 @@ Misc
----
- Remove dependency of settings on audio driver and other (see
fluid_settings_init())
- Add "unselect" command to shell to set a MIDI channel to not sound.
- When specifying -i -s (no console and TCP server) log to TCP clients
with easier parsable messages ("warning:", "error:", etc)
- add function to get initial soundfont generator value
- Pause and resume the synthesizer/audio thread (run synthesizer as a daemon)
- set loop on/off on a sample (set_gen GEN_SAMPLEMODE?)
@ -80,8 +78,6 @@ Shell & command handler
- MIDI file player commands (load/play/stop)
- Allow settings to be loaded before the synthesizer is created
SoundFont Specs:
MIDI Specs
- Omni and poly modes
- sample dump

View File

@ -6,7 +6,7 @@ if ( WIN32 )
set (DEFAULT_SOUNDFONT "C:\\\\soundfonts\\\\default.sf2" CACHE STRING
"Default soundfont file")
else ( WIN32 )
set (DEFAULT_SOUNDFONT "share/soundfonts/default.sf2" CACHE STRING
set (DEFAULT_SOUNDFONT "${CMAKE_INSTALL_PREFIX}/share/soundfonts/default.sf2" CACHE STRING
"Default soundfont file")
endif ( WIN32 )
mark_as_advanced (DEFAULT_SOUNDFONT)

View File

@ -0,0 +1,79 @@
#pragma once
#ifndef PRODUCT_VERSION_MAJOR
#define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@
#endif
#ifndef PRODUCT_VERSION_MINOR
#define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@
#endif
#ifndef PRODUCT_VERSION_PATCH
#define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@
#endif
#ifndef PRODUCT_VERSION_BUILD
#define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@
#endif
#ifndef FILE_VERSION_MAJOR
#define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@
#endif
#ifndef FILE_VERSION_MINOR
#define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@
#endif
#ifndef FILE_VERSION_PATCH
#define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@
#endif
#ifndef FILE_VERSION_BUILD
#define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@
#endif
#ifndef __TO_STRING
#define __TO_STRING_IMPL(x) #x
#define __TO_STRING(x) __TO_STRING_IMPL(x)
#endif
#define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR)
#define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH)
#define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD)
#define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD
#define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0"
#define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR)
#define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH)
#define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD)
#define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD
#define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0"
#ifndef PRODUCT_COMMENTS
#define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0"
#endif
#ifndef PRODUCT_COMPANY_NAME
#define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0"
#endif
#ifndef PRODUCT_COMPANY_COPYRIGHT
#define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0"
#endif
#ifndef PRODUCT_FILE_DESCRIPTION
#define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0"
#endif
#ifndef PRODUCT_INTERNAL_NAME
#define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0"
#endif
#ifndef PRODUCT_ORIGINAL_FILENAME
#define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0"
#endif
#ifndef PRODUCT_BUNDLE
#define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0"
#endif

View File

@ -0,0 +1,37 @@
#include "VersionInfo.h"
#include "winres.h"
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION_RESOURCE
PRODUCTVERSION PRODUCT_VERSION_RESOURCE
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4"
BEGIN
VALUE "Comments", PRODUCT_COMMENTS
VALUE "CompanyName", PRODUCT_COMPANY_NAME
VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_RESOURCE_STR
VALUE "InternalName", PRODUCT_INTERNAL_NAME
VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT
VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_BUNDLE
VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END

View File

@ -0,0 +1,107 @@
include (CMakeParseArguments)
set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR})
# generate_product_version() function
#
# This function uses VersionInfo.in template file and VersionResource.rc file
# to generate WIN32 resource with version information and general resource strings.
#
# Usage:
# generate_product_version(
# SomeOutputResourceVariable
# NAME MyGreatProject
# ICON ${PATH_TO_APP_ICON}
# VERSION_MAJOR 2
# VERSION_MINOR 3
# VERSION_PATH ${BUILD_COUNTER}
# VERSION_REVISION ${BUILD_REVISION}
# )
# where BUILD_COUNTER and BUILD_REVISION could be values from your CI server.
#
# You can use generated resource for your executable targets:
# add_executable(target-name ${target-files} ${SomeOutputResourceVariable})
#
# You can specify resource strings in arguments:
# NAME - name of executable (no defaults, ex: Microsoft Word)
# BUNDLE - bundle (${NAME} is default, ex: Microsoft Office)
# ICON - path to application icon (${CMAKE_SOURCE_DIR}/product.ico by default)
# VERSION_MAJOR - 1 is default
# VERSION_MINOR - 0 is default
# VERSION_PATCH - 0 is default
# VERSION_REVISION - 0 is default
# COMPANY_NAME - your company name (no defaults)
# COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default
# COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default
# ORIGINAL_FILENAME - ${NAME} is default
# INTERNAL_NAME - ${NAME} is default
# FILE_DESCRIPTION - ${NAME} is default
function(generate_product_version outfiles)
set (options)
set (oneValueArgs
NAME
BUNDLE
VERSION_MAJOR
VERSION_MINOR
VERSION_PATCH
VERSION_REVISION
COMPANY_NAME
COMPANY_COPYRIGHT
COMMENTS
ORIGINAL_FILENAME
INTERNAL_NAME
FILE_DESCRIPTION)
set (multiValueArgs)
cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "")
set(PRODUCT_BUNDLE "${PRODUCT_NAME}")
endif()
# if (NOT PRODUCT_ICON OR "${PRODUCT_ICON}" STREQUAL "")
# set(PRODUCT_ICON "${CMAKE_SOURCE_DIR}/product.ico")
# endif()
if (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "")
set(PRODUCT_VERSION_MAJOR 1)
endif()
if (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "")
set(PRODUCT_VERSION_MINOR 0)
endif()
if (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "")
set(PRODUCT_VERSION_PATCH 0)
endif()
if (NOT PRODUCT_VERSION_REVISION OR "${PRODUCT_VERSION_REVISION}" STREQUAL "")
set(PRODUCT_VERSION_REVISION 0)
endif()
if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "")
string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y")
set(PRODUCT_COMPANY_COPYRIGHT "${PRODUCT_COMPANY_NAME} (C) Copyright ${PRODUCT_CURRENT_YEAR}")
endif()
if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "")
set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}")
endif()
if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "")
set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}")
endif()
if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "")
set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}")
endif()
if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "")
set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}")
endif()
set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h)
set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc)
configure_file(
${GenerateProductVersionCurrentDir}/VersionInfo.in
${_VersionInfoFile}
@ONLY)
configure_file(
${GenerateProductVersionCurrentDir}/VersionResource.rc
${_VersionResourceFile}
COPYONLY)
list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile})
set (${outfiles} ${${outfiles}} PARENT_SCOPE)
endfunction()

View File

@ -82,7 +82,7 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = @CMAKE_SOURCE_DIR@/doc/fluidsynth-v11-devdoc.txt @CMAKE_SOURCE_DIR@/include @CMAKE_SOURCE_DIR@/include/fluidsynth @CMAKE_SOURCE_DIR@/src
INPUT = @CMAKE_SOURCE_DIR@/doc/fluidsynth-v11-devdoc.txt @CMAKE_SOURCE_DIR@/include @CMAKE_SOURCE_DIR@/include/fluidsynth @CMAKE_SOURCE_DIR@/src @CMAKE_BINARY_DIR@/include/fluidsynth
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c *.h
RECURSIVE = YES

View File

@ -49,6 +49,14 @@ https://stackoverflow.com/a/6251757
<desc>
(Experimental) Sets the number of synthesis CPU cores. If set to a value greater than 1, then additional synthesis threads will be created to take advantage of a multi CPU or CPU core system. This has the affect of utilizing more of the total CPU for voices or decreasing render times when synthesizing audio to a file.</desc>
</setting>
<setting>
<name>default-soundfont</name>
<type>str</type>
<def>C:\soundfonts\default.sf2 (Windows),<br />
${CMAKE_INSTALL_PREFIX}/share/soundfonts/default.sf2 (all others)</def>
<desc>
The default soundfont file to use by the fluidsynth executable. The default value can be overridden during compilation time by setting the DEFAULT_SOUNDFONT cmake variable.</desc>
</setting>
<setting>
<name>device-id</name>
<type>int</type>
@ -81,6 +89,13 @@ https://stackoverflow.com/a/6251757
<desc>
When set to "yes" the LADSPA subsystem will be enabled. This subsystem allows to load and interconnect LADSPA plug-ins. The output of the synthesizer is processed by the LADSPA subsystem. Note that the synthesizer has to be compiled with LADSPA support. More information about the LADSPA subsystem later.</desc>
</setting>
<setting>
<name>lock-memory</name>
<type>bool</type>
<def>1 (TRUE)</def>
<desc>
Page-lock memory that contains audio sample data, if true.</desc>
</setting>
<setting>
<name>midi-channels</name>
<type>int</type>
@ -258,15 +273,6 @@ https://stackoverflow.com/a/6251757
When set to 1 (TRUE) the synthesizer will print out information about the received MIDI events to the stdout. This can be helpful for debugging. This setting cannot be changed after the synthesizer has started.
</desc>
</setting>
<setting>
<name>volenv</name>
<type>str</type>
<def>emu</def>
<vals>compliant, emu</vals>
<desc>
Specifies the kind of volume envelope processing. This esp. influences the way fluidsynth responses to noteon velocity. The default setting 'emu' causes the envelope to be highly dynamic (i.e. compatible with the EMU10K1). Alternatively this may be set to 'compliant' for a less dynamic envelope, as it was done before fluidsynth 1.0.9. Note that this setting can only be changed until the first synth has been created. Changing it afterwards will have no effect for the rest of fluidsynths lifetime.
</desc>
</setting>
</synth>

View File

@ -83,6 +83,8 @@ Changes in FluidSynth 2.0.0 concerning developers:
- remove deprecated fluid_synth_select_tuning(), use fluid_synth_activate_tuning(synth, chan, bank, prog, FALSE) instead
- remove deprecated fluid_synth_reset_tuning(), use fluid_synth_deactivate_tuning(synth, chan, FALSE) instead
- remove deprecated FLUID_HINT_INTEGER
- remove deprecated fluid_synth_set_gen2() as there doesnt seem to be a use case for absolute generator values
- remove fluid_cmd_handler_register() and fluid_cmd_handler_unregister() from public API, as they seem to be unused downstream
- remove misspelled FLUID_SEQ_PITCHWHHELSENS macro
- remove obsolete "audio.[out|in]put-channels" settings
- remove unimplemented "synth.dump" setting
@ -103,7 +105,6 @@ Changes in FluidSynth 2.0.0 concerning developers:
- use unique device names for the "audio.portaudio.device" setting
- rename fluid_mod_new() and fluid_mod_delete() to match naming conventions: new_fluid_mod() and delete_fluid_mod()
<br /><br />
- add <a href="fluidsettings.xml#synth.volenv">"synth.volenv"</a> a setting for volume envelope processing
- add <a href="fluidsettings.xml#midi.autoconnect">"midi.autoconnect"</a> a setting for automatically connecting fluidsynth to available MIDI input ports
- add <a href="fluidsettings.xml#synth.overflow.important">"synth.overflow.important"</a> and <a href="fluidsettings.xml#synth.overflow.important-channels">"synth.overflow.important-channels"</a> settings to take midi channels during overflow calculation into account that are considered to be "important"
- add support for polyphonic key pressure events, see fluid_event_key_pressure() and fluid_synth_key_pressure()
@ -116,6 +117,8 @@ Changes in FluidSynth 2.0.0 concerning developers:
- expose functions to manipulate the ladspa effects unit (see ladspa.h)
- add support for text and lyrics midi events, see fluid_midi_event_set_lyrics() and fluid_midi_event_set_text()
- add 24 bit sample support, see _fluid_sample_t::data24
- add an additional general-purpose IIR filter, see fluid_synth_set_custom_filter()
- add a custom sinusoidal modulator mapping function, see #FLUID_MOD_SIN
\section NewIn1_1_9 Whats new in 1.1.9?

View File

@ -99,7 +99,12 @@ enum fluid_gen_type {
* is used, however, as the destination for the default pitch wheel
* modulator. */
GEN_PITCH, /**< Pitch @note Not a real SoundFont generator */
GEN_CUSTOM_BALANCE, /**< Balance @note Not a real SoundFont generator */
/* non-standard generator for an additional custom high- or low-pass filter */
GEN_CUSTOM_FILTERFC, /**< Custom filter cutoff frequency */
GEN_CUSTOM_FILTERQ, /**< Custom filter Q */
#ifndef __DOXYGEN__
GEN_LAST /**< @internal Value defines the count of generators (#fluid_gen_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
#endif

View File

@ -49,7 +49,9 @@ enum fluid_mod_flags
FLUID_MOD_CONVEX = 8, /**< Convex mapping function */
FLUID_MOD_SWITCH = 12, /**< Switch (on/off) mapping function */
FLUID_MOD_GC = 0, /**< General controller source type (#fluid_mod_src) */
FLUID_MOD_CC = 16 /**< MIDI CC controller (source will be a MIDI CC number) */
FLUID_MOD_CC = 16, /**< MIDI CC controller (source will be a MIDI CC number) */
FLUID_MOD_SIN = 0x80, /**< Custom non-standard sinus mapping function */
};
/**

View File

@ -138,7 +138,7 @@ FLUIDSYNTH_API
int fluid_settings_dupstr(fluid_settings_t* settings, const char *name, char** str);
FLUIDSYNTH_API
char* fluid_settings_getstr_default(fluid_settings_t* settings, const char *name);
int fluid_settings_getstr_default(fluid_settings_t* settings, const char *name, char** def);
FLUIDSYNTH_API
int fluid_settings_str_equal(fluid_settings_t* settings, const char *name, const char *value);

View File

@ -42,26 +42,6 @@ FLUIDSYNTH_API fluid_ostream_t fluid_get_stdout(void);
FLUIDSYNTH_API char* fluid_get_userconf(char* buf, int len);
FLUIDSYNTH_API char* fluid_get_sysconf(char* buf, int len);
/**
* Command handler function prototype.
* @param data User defined data
* @param ac Argument count
* @param av Array of string arguments
* @param out Output stream to send response to
* @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise
*/
typedef int (*fluid_cmd_func_t)(void* data, int ac, char** av, fluid_ostream_t out);
/**
* Shell command information structure.
*/
typedef struct {
char* name; /**< The name of the command, as typed in the shell */
char* topic; /**< The help topic group of this command */
fluid_cmd_func_t handler; /**< Pointer to the handler for this command */
void* data; /**< User data passed to the handler */
char* help; /**< A help string */
} fluid_cmd_t;
/* The command handler */
@ -74,11 +54,6 @@ void delete_fluid_cmd_handler(fluid_cmd_handler_t* handler);
FLUIDSYNTH_API
void fluid_cmd_handler_set_synth(fluid_cmd_handler_t* handler, fluid_synth_t* synth);
FLUIDSYNTH_API
int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd);
FLUIDSYNTH_API
int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd);
/* Command function */

View File

@ -219,9 +219,6 @@ enum fluid_interp {
FLUIDSYNTH_API int fluid_synth_set_gen (fluid_synth_t* synth, int chan,
int param, float value);
FLUIDSYNTH_API int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan,
int param, float value,
int absolute, int normalized);
FLUIDSYNTH_API float fluid_synth_get_gen(fluid_synth_t* synth, int chan, int param);
@ -313,6 +310,22 @@ FLUIDSYNTH_API void fluid_synth_get_voicelist(fluid_synth_t* synth,
fluid_voice_t* buf[], int bufsize, int ID);
FLUIDSYNTH_API int fluid_synth_handle_midi_event(void* data, fluid_midi_event_t* event);
enum fluid_iir_filter_type {
FLUID_IIR_DISABLED = 0, /**< Custom IIR filter is not operating */
FLUID_IIR_LOWPASS, /**< Custom IIR filter is operating as low-pass filter */
FLUID_IIR_HIGHPASS, /**< Custom IIR filter is operating as high-pass filter */
FLUID_IIR_LAST /**< @internal Value defines the count of filter types (#fluid_iir_filter_type) @warning This symbol is not part of the public API and ABI stability guarantee and may change at any time! */
};
enum fluid_iir_filter_flags {
FLUID_IIR_Q_LINEAR = 1 << 0, /**< The Soundfont spec requires the filter Q to be interpreted in dB. If this flag is set the filter Q is instead assumed to be in a linear range */
FLUID_IIR_Q_ZERO_OFF = 1 << 1, /**< If this flag the filter is switched off if Q == 0 (prior to any transformation) */
FLUID_IIR_NO_GAIN_AMP = 1 << 2 /**< The Soundfont spec requires to correct the gain of the filter depending on the filter's Q. If this flag is set the filter gain will not be corrected. */
};
FLUIDSYNTH_API int fluid_synth_set_custom_filter(fluid_synth_t*, int type, int flags);
/* LADSPA */
FLUIDSYNTH_API fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth);

View File

@ -203,6 +203,23 @@ set ( public_main_HEADER
${CMAKE_SOURCE_DIR}/include/fluidsynth.h
)
if ( WIN32 AND NOT MINGW )
include(generate_product_version)
generate_product_version(
VersionFilesOutputVariable
NAME "Fluidsynth"
BUNDLE "Fluidsynth"
VERSION_MAJOR ${FLUIDSYNTH_VERSION_MAJOR}
VERSION_MINOR ${FLUIDSYNTH_VERSION_MINOR}
VERSION_PATCH ${FLUIDSYNTH_VERSION_MICRO}
VERSION_REVISION 0
COMMENTS "Fluidsynth"
COMPANY_NAME "Fluidsynth LGPL"
ORIGINAL_FILENAME "libfluidsynth.dll"
FILE_DESCRIPTION "Fluidsynth"
)
endif ( WIN32 AND NOT MINGW )
link_directories (
${GLIB_LIBDIR}
${GLIB_LIBRARY_DIRS}
@ -242,6 +259,7 @@ add_library ( libfluidsynth
${libfluidsynth_SOURCES}
${public_HEADERS}
${public_main_HEADER}
${VersionFilesOutputVariable}
)
if ( MACOSX_FRAMEWORK )

View File

@ -60,16 +60,6 @@ struct _fluid_shell_t {
fluid_ostream_t out;
};
/**
* Reduced command information structure for constant data.
* For internal use only.
*/
typedef struct {
const char *name; /**< The name of the command, as typed in the shell */
const char *topic; /**< The help topic group of this command */
fluid_cmd_func_t handler; /**< Pointer to the handler for this command */
const char *help; /**< A help string */
} fluid_cmd_int_t;
static fluid_thread_return_t fluid_shell_run(void* data);
static void fluid_shell_init(fluid_shell_t* shell,
@ -87,7 +77,7 @@ void fluid_shell_settings(fluid_settings_t* settings)
/** the table of all handled commands */
static const fluid_cmd_int_t fluid_commands[] = {
static const fluid_cmd_t fluid_commands[] = {
{ "help", "general", fluid_handle_help,
"help Show help topics ('help TOPIC' for more info)" },
{ "quit", "general", fluid_handle_quit,
@ -1602,8 +1592,8 @@ fluid_handle_info(void* d, int ac, char** av, fluid_ostream_t out)
fluid_ostream_printf(out, "%s:\n", av[0]);
fluid_ostream_printf(out, "Type: string\n");
fluid_ostream_printf(out, "Value: %s\n", s ? s : "NULL");
fluid_ostream_printf(out, "Default value: %s\n",
fluid_settings_getstr_default(settings, av[0]));
fluid_settings_getstr_default(settings, av[0], &s);
fluid_ostream_printf(out, "Default value: %s\n", s);
if (s) FLUID_FREE (s);
@ -2214,7 +2204,7 @@ fluid_expand_path(char* path, char* new_path, int len)
* Command
*/
fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd)
fluid_cmd_t* fluid_cmd_copy(const fluid_cmd_t* cmd)
{
fluid_cmd_t* copy = FLUID_NEW(fluid_cmd_t);
if (copy == NULL) {
@ -2226,7 +2216,6 @@ fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd)
copy->topic = FLUID_STRDUP(cmd->topic);
copy->help = FLUID_STRDUP(cmd->help);
copy->handler = cmd->handler;
copy->data = cmd->data;
return copy;
}
@ -2277,15 +2266,7 @@ fluid_cmd_handler_t* new_fluid_cmd_handler(fluid_synth_t* synth, fluid_midi_rout
if (synth != NULL) {
for (i = 0; i < FLUID_N_ELEMENTS(fluid_commands); i++)
{
fluid_cmd_t cmd = {
(char *)fluid_commands[i].name,
(char *)fluid_commands[i].topic,
fluid_commands[i].handler,
handler,
(char *)fluid_commands[i].help
};
fluid_cmd_handler_register(handler, &cmd);
fluid_cmd_handler_register(handler, &fluid_commands[i]);
}
}
@ -2312,7 +2293,7 @@ delete_fluid_cmd_handler(fluid_cmd_handler_t* handler)
* @return #FLUID_OK if command was inserted, #FLUID_FAILED otherwise
*/
int
fluid_cmd_handler_register(fluid_cmd_handler_t* handler, fluid_cmd_t* cmd)
fluid_cmd_handler_register(fluid_cmd_handler_t* handler, const fluid_cmd_t* cmd)
{
fluid_cmd_t* copy = fluid_cmd_copy(cmd);
fluid_hashtable_insert(handler->commands, copy->name, copy);
@ -2340,7 +2321,7 @@ fluid_cmd_handler_handle(void* data, int ac, char** av, fluid_ostream_t out)
cmd = fluid_hashtable_lookup(handler->commands, av[0]);
if (cmd && cmd->handler)
return (*cmd->handler)(cmd->data, ac - 1, av + 1, out);
return (*cmd->handler)(handler, ac - 1, av + 1, out);
fluid_ostream_printf(out, "unknown command: %s (try help)\n", av[0]);
return FLUID_FAILED;

View File

@ -95,13 +95,35 @@ int fluid_handle_ladspa_stop(void *data, int ac, char **av, fluid_ostream_t out)
int fluid_handle_ladspa_reset(void *data, int ac, char **av, fluid_ostream_t out);
#endif
fluid_cmd_t* fluid_cmd_copy(fluid_cmd_t* cmd);
/**
* Command handler function prototype.
* @param data User defined data
* @param ac Argument count
* @param av Array of string arguments
* @param out Output stream to send response to
* @return Should return #FLUID_OK on success, #FLUID_FAILED otherwise
*/
typedef int (*fluid_cmd_func_t)(void* data, int ac, char** av, fluid_ostream_t out);
/**
* Shell command information structure.
*/
typedef struct {
char* name; /**< The name of the command, as typed in the shell */
char* topic; /**< The help topic group of this command */
fluid_cmd_func_t handler; /**< Pointer to the handler for this command */
char* help; /**< A help string */
} fluid_cmd_t;
fluid_cmd_t* fluid_cmd_copy(const fluid_cmd_t* cmd);
void delete_fluid_cmd(fluid_cmd_t* cmd);
int fluid_cmd_handler_handle(void* data,
int ac, char** av,
fluid_ostream_t out);
int fluid_cmd_handler_register(fluid_cmd_handler_t* handler, const fluid_cmd_t* cmd);
int fluid_cmd_handler_unregister(fluid_cmd_handler_t* handler, const char *cmd);
void fluid_server_remove_client(fluid_server_t* server, fluid_client_t* client);

View File

@ -79,7 +79,8 @@ struct _fluid_jack_midi_driver_t
{
fluid_midi_driver_t driver;
fluid_jack_client_t *client_ref;
jack_port_t *midi_port;
int midi_port_count;
jack_port_t **midi_port; // array of midi port handles
fluid_midi_parser_t *parser;
};
@ -261,15 +262,32 @@ fluid_jack_client_register_ports (void *driver, int isaudio, jack_client_t *clie
if (!isaudio)
{
fluid_jack_midi_driver_t *dev = driver;
dev->midi_port = jack_port_register (client, "midi", JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput | JackPortIsTerminal, 0);
if (!dev->midi_port)
int midi_channels, ports;
fluid_settings_getint(settings, "synth.midi-channels", &midi_channels);
ports = midi_channels / 16;
if((dev->midi_port = FLUID_ARRAY(jack_port_t*, ports)) == NULL)
{
FLUID_LOG (FLUID_ERR, "Failed to create Jack MIDI port");
return FLUID_FAILED;
FLUID_LOG (FLUID_ERR, "Out of memory");
return FLUID_FAILED;
}
for (i = 0; i < ports; i++)
{
FLUID_SNPRINTF(name, sizeof(name), "midi_%02d", i);
dev->midi_port[i] = jack_port_register (client, name, JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput | JackPortIsTerminal, 0);
if (dev->midi_port[i] == NULL)
{
FLUID_LOG (FLUID_ERR, "Failed to create Jack MIDI port");
FLUID_FREE(dev->midi_port);
dev->midi_port = NULL;
return FLUID_FAILED;
}
}
dev->midi_port_count = ports;
return FLUID_OK;
}
@ -528,21 +546,28 @@ fluid_jack_driver_process (jack_nframes_t nframes, void *arg)
if (midi_driver)
{
midi_buffer = jack_port_get_buffer (midi_driver->midi_port, 0);
event_count = jack_midi_get_event_count (midi_buffer);
for (event_index = 0; event_index < event_count; event_index++)
for (i = 0; i < midi_driver->midi_port_count; i++)
{
jack_midi_event_get (&midi_event, midi_buffer, event_index);
midi_buffer = jack_port_get_buffer (midi_driver->midi_port[i], 0);
event_count = jack_midi_get_event_count (midi_buffer);
/* let the parser convert the data into events */
for (u = 0; u < midi_event.size; u++)
{
evt = fluid_midi_parser_parse (midi_driver->parser, midi_event.buffer[u]);
for (event_index = 0; event_index < event_count; event_index++)
{
jack_midi_event_get (&midi_event, midi_buffer, event_index);
/* send the event to the next link in the chain */
if (evt != NULL) midi_driver->driver.handler (midi_driver->driver.data, evt);
}
/* let the parser convert the data into events */
for (u = 0; u < midi_event.size; u++)
{
evt = fluid_midi_parser_parse (midi_driver->parser, midi_event.buffer[u]);
/* send the event to the next link in the chain */
if (evt != NULL)
{
fluid_midi_event_set_channel(evt, fluid_midi_event_get_channel(evt) + i * 16);
midi_driver->driver.handler (midi_driver->driver.data, evt);
}
}
}
}
}
@ -673,7 +698,7 @@ delete_fluid_jack_midi_driver(fluid_midi_driver_t *p)
fluid_jack_client_close (dev->client_ref, dev);
delete_fluid_midi_parser (dev->parser);
FLUID_FREE(dev->midi_port);
FLUID_FREE (dev);
}

View File

@ -151,7 +151,9 @@ static const struct fluid_mdriver_definition_t fluid_midi_drivers[] = {
void fluid_midi_driver_settings(fluid_settings_t* settings)
{
#ifdef FLUID_MIDI_SUPPORT
unsigned int i;
#endif
fluid_settings_register_int (settings, "midi.autoconnect", 0, 0, 1, FLUID_HINT_TOGGLED);

View File

@ -46,6 +46,10 @@ typedef struct {
int buffer_size;
fluid_thread_t *thread;
int cont;
float *left;
float *right;
float *buf;
} fluid_pulse_audio_driver_t;
@ -90,6 +94,9 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings,
char *media_role = NULL;
int realtime_prio = 0;
int err;
float *left = NULL,
*right = NULL,
*buf = NULL;
dev = FLUID_NEW(fluid_pulse_audio_driver_t);
if (dev == NULL) {
@ -156,6 +163,29 @@ new_fluid_pulse_audio_driver2(fluid_settings_t* settings,
FLUID_LOG(FLUID_INFO, "Using PulseAudio driver");
if(func != NULL)
{
left = FLUID_ARRAY(float, period_size);
right = FLUID_ARRAY(float, period_size);
if (left == NULL || right == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
goto error_recovery;
}
}
buf = FLUID_ARRAY(float, period_size * 2);
if(buf == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
goto error_recovery;
}
dev->left = left;
dev->right = right;
dev->buf = buf;
/* Create the audio thread */
dev->thread = new_fluid_thread ("pulse-audio", func ? fluid_pulse_audio_run2 : fluid_pulse_audio_run,
dev, realtime_prio, FALSE);
@ -187,6 +217,10 @@ void delete_fluid_pulse_audio_driver(fluid_audio_driver_t* p)
if (dev->pa_handle)
pa_simple_free(dev->pa_handle);
FLUID_FREE(dev->left);
FLUID_FREE(dev->right);
FLUID_FREE(dev->buf);
FLUID_FREE(dev);
}
@ -195,21 +229,12 @@ static fluid_thread_return_t
fluid_pulse_audio_run(void* d)
{
fluid_pulse_audio_driver_t* dev = (fluid_pulse_audio_driver_t*) d;
float *buf;
float *buf = dev->buf;
int buffer_size;
int err;
buffer_size = dev->buffer_size;
/* FIXME - Probably shouldn't alloc in run() */
buf = FLUID_ARRAY(float, buffer_size * 2);
if (buf == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.");
return FLUID_THREAD_RETURN_VALUE;
}
while (dev->cont)
{
fluid_synth_write_float(dev->data, buffer_size, buf, 0, 2, buf, 1, 2);
@ -222,8 +247,6 @@ fluid_pulse_audio_run(void* d)
}
} /* while (dev->cont) */
FLUID_FREE(buf);
return FLUID_THREAD_RETURN_VALUE;
}
@ -232,7 +255,9 @@ fluid_pulse_audio_run2(void* d)
{
fluid_pulse_audio_driver_t* dev = (fluid_pulse_audio_driver_t*) d;
fluid_synth_t *synth = (fluid_synth_t *)(dev->data);
float *left, *right, *buf;
float *left = dev->left,
*right = dev->right,
*buf = dev->buf;
float* handle[2];
int buffer_size;
int err;
@ -240,20 +265,6 @@ fluid_pulse_audio_run2(void* d)
buffer_size = dev->buffer_size;
/* FIXME - Probably shouldn't alloc in run() */
left = FLUID_ARRAY(float, buffer_size);
right = FLUID_ARRAY(float, buffer_size);
buf = FLUID_ARRAY(float, buffer_size * 2);
if (left == NULL || right == NULL || buf == NULL)
{
FLUID_FREE(left);
FLUID_FREE(right);
FLUID_FREE(buf);
FLUID_LOG(FLUID_ERR, "Out of memory.");
return FLUID_THREAD_RETURN_VALUE;
}
handle[0] = left;
handle[1] = right;
@ -276,10 +287,6 @@ fluid_pulse_audio_run2(void* d)
}
} /* while (dev->cont) */
FLUID_FREE(left);
FLUID_FREE(right);
FLUID_FREE(buf);
return FLUID_THREAD_RETURN_VALUE;
}

View File

@ -188,7 +188,7 @@ settings_foreach_func (void *data, const char *name, int type)
case FLUID_STR_TYPE:
printf ("%-24s STR", name);
defstr = fluid_settings_getstr_default (settings, name);
fluid_settings_getstr_default (settings, name, &defstr);
count = fluid_settings_option_count (settings, name);
if (defstr || count > 0)
@ -263,11 +263,11 @@ int main(int argc, char** argv)
fluid_synth_t* synth = NULL;
#ifdef NETWORK_SUPPORT
fluid_server_t* server = NULL;
int with_server = 0;
#endif
char* config_file = NULL;
int audio_groups = 0;
int audio_channels = 0;
int with_server = 0;
int dump = 0;
int fast_render = 0;
static const char optchars[] = "a:C:c:dE:f:F:G:g:hijK:L:lm:nO:o:p:R:r:sT:Vvz:";
@ -485,7 +485,9 @@ int main(int argc, char** argv)
fluid_settings_setnum(settings, "synth.sample-rate", atof(optarg));
break;
case 's':
#ifdef NETWORK_SUPPORT
with_server = 1;
#endif
break;
case 'T':
if (FLUID_STRCMP (optarg, "help") == 0)
@ -574,7 +576,9 @@ int main(int argc, char** argv)
if (fast_render) {
midi_in = 0;
interactive = 0;
#ifdef NETWORK_SUPPORT
with_server = 0;
#endif
fluid_settings_setstr(settings, "player.timing-source", "sample");
fluid_settings_setint(settings, "synth.lock-memory", 0);
fluid_settings_setint(settings, "synth.parallel-render", 1); /* TODO: Fast_render should not need this, but currently do */
@ -802,15 +806,16 @@ print_usage()
void
print_welcome()
{
printf("FluidSynth version %s\n"
printf("FluidSynth runtime version %s\n"
"Copyright (C) 2000-2017 Peter Hanappe and others.\n"
"Distributed under the LGPL license.\n"
"SoundFont(R) is a registered trademark of E-mu Systems, Inc.\n\n",
FLUIDSYNTH_VERSION);
fluid_version_str());
}
void print_configure()
{
printf("FluidSynth executable version %s\n", FLUIDSYNTH_VERSION);
puts("Sample type="
#ifdef WITH_FLOAT
"float"

View File

@ -479,13 +479,22 @@ static void
fluid_chorus_sine(int *buf, int len, int depth)
{
int i;
double val;
double angle, incr, mult;
/* Pre-calculate increment between angles. */
incr = (2. * M_PI) / (double)len;
/* Pre-calculate 'depth' multiplier. */
mult = (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES;
/* Initialize to zero degrees. */
angle = 0.;
/* Build sine modulation waveform */
for (i = 0; i < len; i++) {
val = sin((double) i / (double)len * 2.0 * M_PI);
buf[i] = (int) ((1.0 + val) * (double) depth / 2.0 * (double) INTERPOLATION_SUBSAMPLES);
buf[i] -= 3* MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
// printf("%i %i\n",i,buf[i]);
buf[i] = (int) ((1. + sin(angle)) * mult) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
angle += incr;
}
}
@ -496,15 +505,25 @@ fluid_chorus_sine(int *buf, int len, int depth)
static void
fluid_chorus_triangle(int *buf, int len, int depth)
{
int i=0;
int ii=len-1;
double val;
double val2;
int *il = buf;
int *ir = buf + len-1;
int ival;
double val, incr;
while (i <= ii){
val = i * 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
val2= (int) (val + 0.5) - 3 * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
buf[i++] = (int) val2;
buf[ii--] = (int) val2;
/* Pre-calculate increment for the ramp. */
incr = 2.0 / len * (double)depth * (double) INTERPOLATION_SUBSAMPLES;
/* Initialize first value */
val = 0. - 3. * MAX_SAMPLES * INTERPOLATION_SUBSAMPLES;
/* Build triangular modulation waveform */
while (il <= ir) {
/* Assume 'val' to be always negative for rounding mode */
ival = (int)(val - 0.5);
*il++ = ival;
*ir-- = ival;
val += incr;
}
}

View File

@ -23,7 +23,12 @@
#include "fluid_conv.h"
/**
* Applies a lowpass filter with variable cutoff frequency and quality factor.
* Applies a low- or high-pass filter with variable cutoff frequency and quality factor
* for a given biquad transfer function:
* b0 + b1*z^-1 + b2*z^-2
* H(z) = ------------------------
* a0 + a1*z^-1 + a2*z^-2
*
* Also modifies filter state accordingly.
* @param iir_filter Filter parameter
* @param dsp_buf Pointer to the synthesized audio data
@ -31,14 +36,12 @@
*/
/*
* Variable description:
* - dsp_a1, dsp_a2, dsp_b0, dsp_b1, dsp_b2: Filter coefficients
* - dsp_a1, dsp_a2: Filter coefficients for the the previously filtered output signal
* - dsp_b0, dsp_b1, dsp_b2: Filter coefficients for input signal
* - coefficients normalized to a0
*
* A couple of variables are used internally, their results are discarded:
* - dsp_i: Index through the output buffer
* - dsp_phase_fractional: The fractional part of dsp_phase
* - dsp_coeff: A table of four coefficients, depending on the fractional phase.
* Used to interpolate between samples.
* - dsp_process_buffer: Holds the processed signal between stages
* - dsp_centernode: delay line for the IIR filter
* - dsp_hist1: same
* - dsp_hist2: same
@ -47,6 +50,12 @@ void
fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
fluid_real_t *dsp_buf, int count)
{
if(iir_filter->type == FLUID_IIR_DISABLED || iir_filter->q_lin == 0)
{
return;
}
else
{
/* IIR filter sample history */
fluid_real_t dsp_hist1 = iir_filter->hist1;
fluid_real_t dsp_hist2 = iir_filter->hist2;
@ -91,10 +100,10 @@ fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
if (dsp_filter_coeff_incr_count-- > 0)
{
fluid_real_t old_b02 = dsp_b02;
dsp_a1 += dsp_a1_incr;
dsp_a2 += dsp_a2_incr;
dsp_b02 += dsp_b02_incr;
dsp_b1 += dsp_b1_incr;
dsp_a1 += dsp_a1_incr;
dsp_a2 += dsp_a2_incr;
dsp_b02 += dsp_b02_incr;
dsp_b1 += dsp_b1_incr;
/* Compensate history to avoid the filter going havoc with large frequency changes */
if (iir_filter->compensate_incr && fabs(dsp_b02) > 0.001) {
@ -125,15 +134,27 @@ fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
iir_filter->filter_coeff_incr_count = dsp_filter_coeff_incr_count;
fluid_check_fpe ("voice_filter");
}
}
void fluid_iir_filter_init(fluid_iir_filter_t* iir_filter, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags)
{
iir_filter->type = type;
iir_filter->flags = flags;
if(type != FLUID_IIR_DISABLED)
{
fluid_iir_filter_reset(iir_filter);
}
}
void
fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter)
{
iir_filter->hist1 = 0;
iir_filter->hist2 = 0;
iir_filter->last_fres = -1.;
iir_filter->q_lin = 0;
iir_filter->filter_startup = 1;
}
@ -145,48 +166,102 @@ fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter,
iir_filter->last_fres = -1.;
}
void
fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter,
fluid_real_t q_dB)
static fluid_real_t fluid_iir_filter_q_from_dB(fluid_real_t q_dB)
{
/* The generator contains 'centibels' (1/10 dB) => divide by 10 to
* obtain dB */
q_dB /= 10.0f;
/* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
fluid_clip(q_dB, 0.0f, 96.0f);
/* Short version: Modify the Q definition in a way, that a Q of 0
* dB leads to no resonance hump in the freq. response.
*
* Long version: From SF2.01, page 39, item 9 (initialFilterQ):
* "The gain at the cutoff frequency may be less than zero when
* zero is specified". Assume q_dB=0 / q_lin=1: If we would leave
* q as it is, then this results in a 3 dB hump slightly below
* fc. At fc, the gain is exactly the DC gain (0 dB). What is
* (probably) meant here is that the filter does not show a
* resonance hump for q_dB=0. In this case, the corresponding
* q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of
* attenuation at fc now. In this case Q_dB is the height of the
* resonance peak not over the DC gain, but over the frequency
* response of a non-resonant filter. This idea is implemented as
* follows: */
q_dB -= 3.01f;
/* The 'sound font' Q is defined in dB. The filter needs a linear
q. Convert. */
iir_filter->q_lin = (fluid_real_t) (pow(10.0f, q_dB / 20.0f));
/* SF 2.01 page 59:
*
* The SoundFont specs ask for a gain reduction equal to half the
* height of the resonance peak (Q). For example, for a 10 dB
* resonance peak, the gain is reduced by 5 dB. This is done by
* multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB
* by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc)
* The gain is later factored into the 'b' coefficients
* (numerator of the filter equation). This gain factor depends
* only on Q, so this is the right place to calculate it.
*/
iir_filter->filter_gain = (fluid_real_t) (1.0 / sqrt(iir_filter->q_lin));
/* The synthesis loop will have to recalculate the filter coefficients. */
iir_filter->last_fres = -1.;
return pow(10.0f, q_dB / 20.0f);
}
void
fluid_iir_filter_set_q(fluid_iir_filter_t* iir_filter, fluid_real_t q)
{
int flags = iir_filter->flags;
if(flags & FLUID_IIR_Q_ZERO_OFF && q<=0.0)
{
q = 0;
}
else if(flags & FLUID_IIR_Q_LINEAR)
{
/* q is linear (only for user-defined filter)
* increase to avoid Q being somewhere between zero and one,
* which results in some strange amplified lowpass signal
*/
q++;
}
else
{
q = fluid_iir_filter_q_from_dB(q);
}
iir_filter->q_lin = q;
iir_filter->filter_gain = 1.0;
if(!(flags & FLUID_IIR_NO_GAIN_AMP))
{
/* SF 2.01 page 59:
*
* The SoundFont specs ask for a gain reduction equal to half the
* height of the resonance peak (Q). For example, for a 10 dB
* resonance peak, the gain is reduced by 5 dB. This is done by
* multiplying the total gain with sqrt(1/Q). `Sqrt' divides dB
* by 2 (100 lin = 40 dB, 10 lin = 20 dB, 3.16 lin = 10 dB etc)
* The gain is later factored into the 'b' coefficients
* (numerator of the filter equation). This gain factor depends
* only on Q, so this is the right place to calculate it.
*/
iir_filter->filter_gain /= sqrt(q);
}
/* The synthesis loop will have to recalculate the filter coefficients. */
iir_filter->last_fres = -1.;
}
static FLUID_INLINE void
fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter,
int transition_samples,
fluid_real_t output_rate)
{
/* FLUID_IIR_Q_LINEAR may switch the filter off by setting Q==0 */
if(iir_filter->q_lin == 0)
{
return;
}
else
{
/*
* Those equations from Robert Bristow-Johnson's `Cookbook
* formulae for audio EQ biquad filter coefficients', obtained
* from Harmony-central.com / Computer / Programming. They are
* the result of the bilinear transform on an analogue filter
* prototype. To quote, `BLT frequency warping has been taken
* into account for both significant frequency relocation and for
* bandwidth readjustment'. */
* Those equations from Robert Bristow-Johnson's `Cookbook
* formulae for audio EQ biquad filter coefficients', obtained
* from Harmony-central.com / Computer / Programming. They are
* the result of the bilinear transform on an analogue filter
* prototype. To quote, `BLT frequency warping has been taken
* into account for both significant frequency relocation and for
* bandwidth readjustment'. */
fluid_real_t omega = (fluid_real_t) (2.0 * M_PI *
(iir_filter->last_fres / ((float) output_rate)));
@ -204,11 +279,33 @@ fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter,
* iir_filter->b1=(1.-cos_coeff)*a0_inv*iir_filter->filter_gain;
* iir_filter->b2=(1.-cos_coeff)*a0_inv*0.5*iir_filter->filter_gain; */
/* "a" coeffs are same for all 3 available filter types */
fluid_real_t a1_temp = -2.0f * cos_coeff * a0_inv;
fluid_real_t a2_temp = (1.0f - alpha_coeff) * a0_inv;
fluid_real_t b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain;
/* both b0 -and- b2 */
fluid_real_t b02_temp = b1_temp * 0.5f;
fluid_real_t b02_temp, b1_temp;
switch(iir_filter->type)
{
case FLUID_IIR_HIGHPASS:
b1_temp = (1.0f + cos_coeff) * a0_inv * iir_filter->filter_gain;
/* both b0 -and- b2 */
b02_temp = b1_temp * 0.5f;
b1_temp *= -1.0f;
break;
case FLUID_IIR_LOWPASS:
b1_temp = (1.0f - cos_coeff) * a0_inv * iir_filter->filter_gain;
/* both b0 -and- b2 */
b02_temp = b1_temp * 0.5f;
break;
default:
/* filter disabled, should never get here */
return;
}
iir_filter->compensate_incr = 0;
@ -249,6 +346,7 @@ fluid_iir_filter_calculate_coefficients(fluid_iir_filter_t* iir_filter,
iir_filter->filter_coeff_incr_count = transition_samples;
}
fluid_check_fpe ("voice_write filter calculation");
}
}
@ -280,7 +378,7 @@ void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter,
fres = 5;
/* if filter enabled and there is a significant frequency change.. */
if (fabs (fres - iir_filter->last_fres) > 0.01)
if (iir_filter->type != FLUID_IIR_DISABLED && fabs (fres - iir_filter->last_fres) > 0.01)
{
/* The filter coefficients have to be recalculated (filter
* parameters have changed). Recalculation for various reasons is

View File

@ -26,28 +26,32 @@
typedef struct _fluid_iir_filter_t fluid_iir_filter_t;
void fluid_iir_filter_init(fluid_iir_filter_t* iir_filter, enum fluid_iir_filter_type, enum fluid_iir_filter_flags flags);
void fluid_iir_filter_apply(fluid_iir_filter_t* iir_filter,
fluid_real_t *dsp_buf, int dsp_buf_count);
fluid_real_t *dsp_buf, int dsp_buf_count);
void fluid_iir_filter_reset(fluid_iir_filter_t* iir_filter);
void fluid_iir_filter_set_q_dB(fluid_iir_filter_t* iir_filter,
fluid_real_t q_dB);
void fluid_iir_filter_set_q(fluid_iir_filter_t* iir_filter, fluid_real_t q);
void fluid_iir_filter_set_fres(fluid_iir_filter_t* iir_filter,
fluid_real_t fres);
void fluid_iir_filter_calc(fluid_iir_filter_t* iir_filter,
fluid_real_t output_rate,
fluid_real_t fres_mod);
fluid_real_t fres_mod);
/* We can't do information hiding here, as fluid_voice_t includes the struct
without a pointer. */
struct _fluid_iir_filter_t
{
enum fluid_iir_filter_type type; /* specifies the type of this filter */
enum fluid_iir_filter_flags flags; /* additional flags to customize this filter */
/* filter coefficients */
/* The coefficients are normalized to a0. */
/* b0 and b2 are identical => b02 */
/* b0 and b2 are identical => b02 */
fluid_real_t b02; /* b0 / a0 */
fluid_real_t b1; /* b1 / a0 */
fluid_real_t a1; /* a0 / a0 */

View File

@ -38,7 +38,7 @@ fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
/* the envelope is in the attack section: ramp linearly to max value.
* A positive modlfo_to_vol should increase volume (negative attenuation).
*/
target_amp = fluid_atten2amp (voice->dsp.attenuation)
target_amp = fluid_cb2amp (voice->dsp.attenuation)
* fluid_cb2amp (fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol)
* fluid_adsr_env_get_val(&voice->envlfo.volenv);
}
@ -47,7 +47,7 @@ fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
fluid_real_t amplitude_that_reaches_noise_floor;
fluid_real_t amp_max;
target_amp = fluid_atten2amp (voice->dsp.attenuation)
target_amp = fluid_cb2amp (voice->dsp.attenuation)
* fluid_cb2amp (960.0f * (1.0f - fluid_adsr_env_get_val(&voice->envlfo.volenv))
+ fluid_lfo_get_val(&voice->envlfo.modlfo) * -voice->envlfo.modlfo_to_vol);
@ -74,7 +74,7 @@ fluid_rvoice_calc_amp(fluid_rvoice_t* voice)
* volenv_val can only drop):
*/
amp_max = fluid_atten2amp (voice->dsp.min_attenuation_cB) *
amp_max = fluid_cb2amp (voice->dsp.min_attenuation_cB) *
fluid_adsr_env_get_val(&voice->envlfo.volenv);
/* And if amp_max is already smaller than the known amplitude,
@ -361,12 +361,17 @@ fluid_rvoice_write (fluid_rvoice_t* voice, fluid_real_t *dsp_buf)
return count;
/*************** resonant filter ******************/
fluid_iir_filter_calc(&voice->resonant_filter, voice->dsp.output_rate,
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_fc +
fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
/* additional custom filter - only uses the fixed modulator, no lfos... */
fluid_iir_filter_calc(&voice->resonant_custom_filter, voice->dsp.output_rate, 0);
fluid_iir_filter_apply(&voice->resonant_custom_filter, dsp_buf, count);
return count;
}
@ -487,6 +492,7 @@ fluid_rvoice_reset(fluid_rvoice_t* voice)
/* Clear sample history in filter */
fluid_iir_filter_reset(&voice->resonant_filter);
fluid_iir_filter_reset(&voice->resonant_custom_filter);
/* Force setting of the phase at the first DSP loop run
* This cannot be done earlier, because it depends on modulators.

View File

@ -36,9 +36,10 @@ typedef struct _fluid_rvoice_t fluid_rvoice_t;
/* Smallest amplitude that can be perceived (full scale is +/- 0.5)
* 16 bits => 96+4=100 dB dynamic range => 0.00001
* 0.00001 * 2 is approximately 0.00003 :)
* 24 bits => 144-4 = 140 dB dynamic range => 1.e-7
* 1.e-7 * 2 == 2.e-7 :)
*/
#define FLUID_NOISE_FLOOR 0.00003
#define FLUID_NOISE_FLOOR 2.e-7
enum fluid_loop {
@ -150,6 +151,7 @@ struct _fluid_rvoice_t
fluid_rvoice_envlfo_t envlfo;
fluid_rvoice_dsp_t dsp;
fluid_iir_filter_t resonant_filter; /* IIR resonant dsp filter */
fluid_iir_filter_t resonant_custom_filter; /* optional custom/general-purpose IIR resonant filter */
fluid_rvoice_buffers_t buffers;
};
@ -197,4 +199,27 @@ int fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice);
int fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice);
int fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice);
/*
* Combines the most significant 16 bit part of a sample with a potentially present
* least sig. 8 bit part in order to create a 24 bit sample.
*/
static FLUID_INLINE int32_t
fluid_rvoice_get_sample(const short int* dsp_msb, const char* dsp_lsb, unsigned int idx)
{
/* cast sample to unsigned type, so we can safely shift and bitwise or
* without relying on undefined behaviour (should never happen anyway ofc...) */
uint32_t msb = (uint32_t)dsp_msb[idx];
uint8_t lsb = 0U;
/* most soundfonts have 16 bit samples, assume that it's unlikely we
* experience 24 bit samples here */
if(FLUID_UNLIKELY(dsp_lsb != NULL))
{
lsb = (uint8_t)dsp_lsb[idx];
}
return (int32_t)((msb << 8) | lsb);
}
#endif

View File

@ -119,26 +119,11 @@ void fluid_rvoice_dsp_config (void)
fluid_check_fpe("interpolation table calculation");
}
/*
* Combines the most significant 16 bit part of a sample with a potentially present
* least sig. 8 bit part in order to create a 24 bit sample.
*/
static FLUID_INLINE fluid_real_t
fluid_rvoice_get_sample(const short int* dsp_msb, const char* dsp_lsb, unsigned int idx)
fluid_rvoice_get_float_sample(const short int* dsp_msb, const char* dsp_lsb, unsigned int idx)
{
/* cast sample to unsigned type, so we can safely shift and bitwise or
* without relying on undefined behaviour (should never happen anyway ofc...) */
uint32_t msb = (uint32_t)dsp_msb[idx];
uint8_t lsb = 0U;
/* most soundfonts have 16 bit samples, assume that it's unlikely we
* experience 24 bit samples here */
if(FLUID_UNLIKELY(dsp_lsb != NULL))
{
lsb = (uint8_t)dsp_lsb[idx];
}
return (fluid_real_t)((int32_t)((msb << 8) | lsb));
int32_t sample = fluid_rvoice_get_sample(dsp_msb, dsp_lsb, idx);
return (fluid_real_t)sample;
}
/* No interpolation. Just take the sample, which is closest to
@ -174,7 +159,7 @@ fluid_rvoice_dsp_interpolate_none (fluid_rvoice_dsp_t *voice)
/* interpolate sequence of sample points */
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
dsp_buf[dsp_i] = dsp_amp * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index);
dsp_buf[dsp_i] = dsp_amp * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index);
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -233,8 +218,8 @@ fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
end_index = (looping ? voice->loopend - 1 : voice->end) - 1;
/* 2nd interpolation point to use at end of loop or sample */
if (looping) point = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */
else point = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->end); /* duplicate end for samples no longer looping */
if (looping) point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart); /* loop start */
else point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end); /* duplicate end for samples no longer looping */
while (1)
{
@ -244,8 +229,8 @@ fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
for ( ; dsp_i < FLUID_BUFSIZE && dsp_phase_index <= end_index; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1));
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -262,7 +247,7 @@ fluid_rvoice_dsp_interpolate_linear (fluid_rvoice_dsp_t *voice)
for (; dsp_phase_index <= end_index && dsp_i < FLUID_BUFSIZE; dsp_i++)
{
coeffs = interp_coeff_linear[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
dsp_buf[dsp_i] = dsp_amp * (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[1] * point);
/* increment phase and amplitude */
@ -325,23 +310,23 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_point = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */
start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1); /* last point in loop (wrap around) */
}
else
{
start_index = voice->start;
start_point = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */
start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the point */
}
/* get points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_point1 = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart);
end_point2 = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart + 1);
end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart);
end_point2 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1);
}
else
{
end_point1 = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->end);
end_point1 = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end);
end_point2 = end_point1;
}
@ -355,9 +340,9 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp *
( coeffs[0] * start_point
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2));
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -370,10 +355,10 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp *
( coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2));
( coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -391,9 +376,9 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp *
( coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
( coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[3] * end_point1);
/* increment phase and amplitude */
@ -409,8 +394,8 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
{
coeffs = interp_coeff[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp *
( coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
( coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[2] * end_point1
+ coeffs[3] * end_point2);
@ -431,7 +416,7 @@ fluid_rvoice_dsp_interpolate_4th_order (fluid_rvoice_dsp_t *voice)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_point = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend-1);
start_point = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend-1);
}
}
@ -484,14 +469,14 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
if (voice->has_looped) /* set start_index and start point if looped or not */
{
start_index = voice->loopstart;
start_points[0] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 1);
start_points[1] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 2);
start_points[2] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 3);
start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1);
start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2);
start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3);
}
else
{
start_index = voice->start;
start_points[0] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the start point */
start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->start); /* just duplicate the start point */
start_points[1] = start_points[0];
start_points[2] = start_points[0];
}
@ -499,13 +484,13 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
/* get the 3 points off the end (loop start if looping, duplicate point if end) */
if (looping)
{
end_points[0] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart);
end_points[1] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart + 1);
end_points[2] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopstart + 2);
end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart);
end_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 1);
end_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopstart + 2);
}
else
{
end_points[0] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->end);
end_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->end);
end_points[1] = end_points[0];
end_points[2] = end_points[0];
}
@ -523,10 +508,10 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
* (coeffs[0] * start_points[2]
+ coeffs[1] * start_points[1]
+ coeffs[2] * start_points[0]
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+3));
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+3));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -544,11 +529,11 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * start_points[1]
+ coeffs[1] * start_points[0]
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+3));
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+3));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -565,12 +550,12 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * start_points[0]
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+3));
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+3));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -587,13 +572,13 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+3));
* (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+3));
/* increment phase and amplitude */
fluid_phase_incr (dsp_phase, dsp_phase_incr);
@ -612,12 +597,12 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+2)
* (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+2)
+ coeffs[6] * end_points[0]);
/* increment phase and amplitude */
@ -634,11 +619,11 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index+1)
* (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index+1)
+ coeffs[5] * end_points[0]
+ coeffs[6] * end_points[1]);
@ -656,10 +641,10 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
coeffs = sinc_table7[fluid_phase_fract_to_tablerow (dsp_phase)];
dsp_buf[dsp_i] = dsp_amp
* (coeffs[0] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_sample(dsp_data, dsp_data24, dsp_phase_index)
* (coeffs[0] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-3)
+ coeffs[1] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-2)
+ coeffs[2] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index-1)
+ coeffs[3] * fluid_rvoice_get_float_sample(dsp_data, dsp_data24, dsp_phase_index)
+ coeffs[4] * end_points[0]
+ coeffs[5] * end_points[1]
+ coeffs[6] * end_points[2]);
@ -681,9 +666,9 @@ fluid_rvoice_dsp_interpolate_7th_order (fluid_rvoice_dsp_t *voice)
{
voice->has_looped = 1;
start_index = voice->loopstart;
start_points[0] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 1);
start_points[1] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 2);
start_points[2] = fluid_rvoice_get_sample(dsp_data, dsp_data24, voice->loopend - 3);
start_points[0] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 1);
start_points[1] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 2);
start_points[2] = fluid_rvoice_get_float_sample(dsp_data, dsp_data24, voice->loopend - 3);
}
}

View File

@ -109,8 +109,9 @@ fluid_rvoice_event_dispatch(fluid_rvoice_event_t* event)
EVENTFUNC_I1(fluid_lfo_set_delay, fluid_lfo_t*);
EVENTFUNC_R1(fluid_lfo_set_incr, fluid_lfo_t*);
EVENTFUNC_II(fluid_iir_filter_init, fluid_iir_filter_t*);
EVENTFUNC_R1(fluid_iir_filter_set_fres, fluid_iir_filter_t*);
EVENTFUNC_R1(fluid_iir_filter_set_q_dB, fluid_iir_filter_t*);
EVENTFUNC_R1(fluid_iir_filter_set_q, fluid_iir_filter_t*);
EVENTFUNC_II(fluid_rvoice_buffers_set_mapping, fluid_rvoice_buffers_t*);
EVENTFUNC_IR(fluid_rvoice_buffers_set_amp, fluid_rvoice_buffers_t*);

View File

@ -30,6 +30,13 @@
#include <sndfile.h>
#endif
/* EMU8k/10k hardware applies this factor to initial attenuation generator values set at preset and
* instrument level in a soundfont. We apply this factor when loading the generator values to stay
* compatible as most existing soundfonts expect exactly this (strange, non-standard) behaviour. */
#define EMU_ATTENUATION_FACTOR (0.4f)
/***************************************************************
*
* SFONT LOADER
@ -1289,6 +1296,12 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t* zone, SFZone *sfzone, fluid_
zone->vello = (int) sfgen->amount.range.lo;
zone->velhi = (int) sfgen->amount.range.hi;
break;
case GEN_ATTENUATION:
/* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
* preset and instrument level */
zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
zone->gen[sfgen->id].flags = GEN_SET;
break;
default:
/* FIXME: some generators have an unsigne word amount value but i don't know which ones */
zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword;
@ -1690,6 +1703,12 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t* zone, SFZone *sfzone, fluid_defs
zone->vello = (int) sfgen->amount.range.lo;
zone->velhi = (int) sfgen->amount.range.hi;
break;
case GEN_ATTENUATION:
/* EMU8k/10k hardware applies a scale factor to initial attenuation generator values set at
* preset and instrument level */
zone->gen[sfgen->id].val = (fluid_real_t) sfgen->amount.sword * EMU_ATTENUATION_FACTOR;
zone->gen[sfgen->id].flags = GEN_SET;
break;
default:
/* FIXME: some generators have an unsigned word amount value but
i don't know which ones */
@ -3432,7 +3451,7 @@ fixup_sample (SFData * sf)
int invalid_loops=FALSE;
int invalid_loopstart;
int invalid_loopend, loopend_end_mismatch;
unsigned int sdtachunk_size = sf->samplesize;
unsigned int total_samples = sf->samplesize / FLUID_MEMBER_SIZE(fluid_defsfont_t, sampledata[0]);
p = sf->sample;
while (p)
@ -3445,14 +3464,14 @@ fixup_sample (SFData * sf)
* this is as it should be. however we cannot be sure whether any of sam.loopend or sam.end
* is correct. hours of thinking through this have concluded, that it would be best practice
* to mangle with loops as little as necessary by only making sure loopend is within
* sdtachunk_size. incorrect soundfont shall preferably fail loudly. */
invalid_loopend = (sam->loopend > sdtachunk_size) || (sam->loopstart >= sam->loopend);
* total_samples. incorrect soundfont shall preferably fail loudly. */
invalid_loopend = (sam->loopend > total_samples) || (sam->loopstart >= sam->loopend);
loopend_end_mismatch = (sam->loopend > sam->end);
/* if sample is not a ROM sample and end is over the sample data chunk
or sam start is greater than 4 less than the end (at least 4 samples) */
if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM) && sam->end > sdtachunk_size)
if ((!(sam->sampletype & FLUID_SAMPLETYPE_ROM) && sam->end > total_samples)
|| sam->start > (sam->end - 4))
{
FLUID_LOG (FLUID_WARN, _("Sample '%s' start/end file positions are invalid,"
@ -3494,8 +3513,7 @@ fixup_sample (SFData * sf)
* valid sample will be played */
sam->loopend = sam->end;
}
if(loopend_end_mismatch)
else if(loopend_end_mismatch)
{
FLUID_LOG (FLUID_DBG, _("Sample '%s' has invalid loop stop '%d',"
" sample stop at '%d', using it anyway"), sam->name, sam->loopend, sam->end);

View File

@ -86,7 +86,9 @@ static const fluid_gen_info_t fluid_gen_info[] = {
{ GEN_EXCLUSIVECLASS, 0, 0, 0.0f, 0.0f, 0.0f },
{ GEN_OVERRIDEROOTKEY, 1, 0, 0.0f, 127.0f, -1.0f },
{ GEN_PITCH, 1, 0, 0.0f, 127.0f, 0.0f },
{ GEN_CUSTOM_BALANCE, 1, 0, -960.0f, 960.0f, 0.0f }
{ GEN_CUSTOM_BALANCE, 1, 0, -960.0f, 960.0f, 0.0f },
{ GEN_CUSTOM_FILTERFC, 1, 2, 0.0f, 22050.0f, 0.0f },
{ GEN_CUSTOM_FILTERQ, 1, 1, 0.0f, 960.0f, 0.0f }
};

View File

@ -300,6 +300,30 @@ fluid_mod_transform_source_value(fluid_real_t val, unsigned char mod_flags, cons
case FLUID_MOD_SWITCH | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* =15 */
val = (val_norm >= 0.5f)? -1.0f : 1.0f;
break;
/*
* MIDI CCs only have a resolution of 7 bits. The closer val_norm gets to 1,
* the less will be the resulting change of the sinus. When using this sin()
* for scaling the cutoff frequency, there will be no audible difference between
* MIDI CCs 118 to 127. To avoid this waste of CCs multiply with 0.87
* (at least for unipolar) which makes sin() never get to 1.0 but to 0.98 which
* is close enough.
*/
case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_POSITIVE: /* custom sin(x) */
val = sin(M_PI/2 * val_norm * 0.87);
break;
case FLUID_MOD_SIN | FLUID_MOD_UNIPOLAR | FLUID_MOD_NEGATIVE: /* custom */
val = sin(M_PI/2 * (1.0f - val_norm) * 0.87);
break;
case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_POSITIVE: /* custom */
val = (val_norm > 0.5f) ? sin(M_PI/2 * 2 * (val_norm - 0.5f))
: -sin(M_PI/2 * 2 * (0.5f - val_norm));
break;
case FLUID_MOD_SIN | FLUID_MOD_BIPOLAR | FLUID_MOD_NEGATIVE: /* custom */
val = (val_norm > 0.5f) ? -sin(M_PI/2 * 2 * (val_norm - 0.5f))
: sin(M_PI/2 * 2 * (0.5f - val_norm));
break;
default:
FLUID_LOG(FLUID_ERR, "Unknown modulator type '%d', disabling modulator.", mod_flags);
val = 0.0f;
@ -510,6 +534,8 @@ void fluid_dump_modulator(fluid_mod_t * mod){
switch(dest){
case GEN_FILTERQ: printf("Q"); break;
case GEN_FILTERFC: printf("fc"); break;
case GEN_CUSTOM_FILTERQ: printf("custom-Q"); break;
case GEN_CUSTOM_FILTERFC: printf("custom-fc"); break;
case GEN_VIBLFOTOPITCH: printf("VibLFO-to-pitch"); break;
case GEN_MODENVTOPITCH: printf("ModEnv-to-pitch"); break;
case GEN_MODLFOTOPITCH: printf("ModLFO-to-pitch"); break;

View File

@ -140,6 +140,7 @@ static fluid_mod_t default_chorus_mod; /* SF2.01 section 8.4.9 */
static fluid_mod_t default_pitch_bend_mod; /* SF2.01 section 8.4.10 */
static fluid_mod_t custom_balance_mod; /* Non-standard modulator */
/* reverb presets */
static const fluid_revmodel_presets_t revmodel_preset[] = {
/* name */ /* roomsize */ /* damp */ /* width */ /* level */
@ -197,10 +198,6 @@ void fluid_synth_settings(fluid_settings_t* settings)
fluid_settings_add_option(settings, "synth.midi-bank-select", "gs");
fluid_settings_add_option(settings, "synth.midi-bank-select", "xg");
fluid_settings_add_option(settings, "synth.midi-bank-select", "mma");
fluid_settings_register_str(settings, "synth.volenv", "emu", 0);
fluid_settings_add_option(settings, "synth.volenv", "emu");
fluid_settings_add_option(settings, "synth.volenv", "compliant");
}
/**
@ -542,25 +539,6 @@ new_fluid_synth(fluid_settings_t *settings)
/* initialize all the conversion tables and other stuff */
if (fluid_atomic_int_compare_and_exchange(&fluid_synth_initialized, 0, 1))
{
char buf[64];
if (fluid_settings_str_equal (settings, "synth.volenv", "compliant"))
{
fluid_conversion_set_atten_power(FLUID_ATTEN_POWER_DEFAULT_COMPLIANT);
}
else if (fluid_settings_str_equal (settings, "synth.volenv", "emu"))
{
fluid_conversion_set_atten_power(FLUID_ATTEN_POWER_DEFAULT_EMU);
}
else
{
if (fluid_settings_copystr(settings, "synth.volenv", buf, sizeof(buf)) == FLUID_OK)
{
double atten = atof(buf);
if(atten != 0.0)
fluid_conversion_set_atten_power(atten);
}
}
fluid_synth_init();
}
@ -591,7 +569,7 @@ new_fluid_synth(fluid_settings_t *settings)
fluid_settings_getnum_float(settings, "synth.gain", &synth->gain);
fluid_settings_getint(settings, "synth.device-id", &synth->device_id);
fluid_settings_getint(settings, "synth.cpu-cores", &synth->cores);
fluid_settings_getnum_float(settings, "synth.overflow.percussion", &synth->overflow.percussion);
fluid_settings_getnum_float(settings, "synth.overflow.released", &synth->overflow.released);
fluid_settings_getnum_float(settings, "synth.overflow.sustained", &synth->overflow.sustained);
@ -761,7 +739,7 @@ new_fluid_synth(fluid_settings_t *settings)
goto error_recovery;
}
}
fluid_synth_set_sample_rate(synth, synth->sample_rate);
fluid_synth_update_mixer(synth, fluid_rvoice_mixer_set_polyphony,
synth->polyphony, 0.0f);
@ -2528,6 +2506,8 @@ fluid_synth_update_polyphony_LOCAL(fluid_synth_t* synth, int new_polyphony)
synth->voice[i] = new_fluid_voice(synth->sample_rate);
if (synth->voice[i] == NULL)
return FLUID_FAILED;
fluid_voice_set_custom_filter(synth->voice[i], synth->custom_filter_type, synth->custom_filter_flags);
}
synth->nvoice = new_polyphony;
}
@ -5194,6 +5174,41 @@ fluid_ladspa_fx_t *fluid_synth_get_ladspa_fx(fluid_synth_t *synth)
return synth->ladspa_fx;
}
/**
* Configure a general-purpose IIR biquad filter.
*
* This is an optional, additional filter that operates independently from the default low-pass filter required by the Soundfont2 standard.
* By default this filter is off (#FLUID_IIR_DISABLED).
*
* @param synth FluidSynth instance
* @param type Type of the IIR filter to use (see #fluid_iir_filter_type)
* @param flags Additional flags to customize this filter or zero to stay with the default (see #fluid_iir_filter_flags)
*
* @return #FLUID_OK if the settings have been successfully applied, otherwise #FLUID_FAILED
*/
int fluid_synth_set_custom_filter(fluid_synth_t* synth, int type, int flags)
{
int i;
fluid_voice_t *voice;
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(type >= FLUID_IIR_DISABLED && type < FLUID_IIR_LAST, FLUID_FAILED);
fluid_synth_api_enter(synth);
synth->custom_filter_type = type;
synth->custom_filter_flags = flags;
for (i = 0; i < synth->polyphony; i++)
{
voice = synth->voice[i];
fluid_voice_set_custom_filter(voice, type, flags);
}
FLUID_API_RETURN(FLUID_OK);
}
/**
* Set the important channels for voice overflow priority calculation.
*

View File

@ -166,6 +166,8 @@ struct _fluid_synth_t
fluid_mod_t* default_mod; /**< the (dynamic) list of default modulators */
fluid_ladspa_fx_t* ladspa_fx; /**< Effects unit for LADSPA support */
enum fluid_iir_filter_type custom_filter_type; /**< filter type of the user-defined filter currently used for all voices */
enum fluid_iir_filter_flags custom_filter_flags; /**< filter type of the user-defined filter currently used for all voices */
};
fluid_preset_t* fluid_synth_find_preset(fluid_synth_t* synth,
@ -201,6 +203,9 @@ void fluid_synth_api_exit(fluid_synth_t* synth);
void fluid_synth_process_event_queue(fluid_synth_t* synth);
int fluid_synth_set_gen2 (fluid_synth_t* synth, int chan,
int param, float value,
int absolute, int normalized);
/*
* misc
*/

View File

@ -36,6 +36,9 @@
/* min vol envelope release (to stop clicks) in SoundFont timecents */
#define FLUID_MIN_VOLENVRELEASE -7200.0f /* ~16ms */
static const int32_t INT24_MAX = (1 << (16+8-1));
static int fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice);
static int calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
int gen_key2base, int is_decay);
@ -98,6 +101,8 @@ fluid_voice_get_lower_boundary_for_attenuation(fluid_voice_t* voice);
#define UPDATE_RVOICE_R1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, voice->rvoice, arg1)
#define UPDATE_RVOICE_I1(proc, arg1) UPDATE_RVOICE_GENERIC_I1(proc, voice->rvoice, arg1)
#define UPDATE_RVOICE_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_filter, arg1)
#define UPDATE_RVOICE_CUSTOM_FILTER1(proc, arg1) UPDATE_RVOICE_GENERIC_R1(proc, &voice->rvoice->resonant_custom_filter, arg1)
#define UPDATE_RVOICE_CUSTOM_FILTER_I2(proc, arg1, arg2) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->resonant_custom_filter, arg1, arg2)
#define UPDATE_RVOICE2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, voice->rvoice, iarg, rarg)
#define UPDATE_RVOICE_BUFFERS2(proc, iarg, rarg) UPDATE_RVOICE_GENERIC_IR(proc, &voice->rvoice->buffers, iarg, rarg)
@ -172,6 +177,9 @@ static void fluid_voice_initialize_rvoice(fluid_voice_t* voice)
0xffffffff, 1.0f, 0.0f, -1.0f, 2.0f);
fluid_voice_update_modenv(voice, FLUID_VOICE_ENVFINISHED,
0xffffffff, 0.0f, 0.0f, -1.0f, 1.0f);
fluid_iir_filter_init(&voice->rvoice->resonant_filter, FLUID_IIR_LOWPASS, 0);
fluid_iir_filter_init(&voice->rvoice->resonant_custom_filter, FLUID_IIR_DISABLED, 0);
}
/*
@ -203,8 +211,8 @@ new_fluid_voice(fluid_real_t output_rate)
voice->sample = NULL;
/* Initialize both the rvoice and overflow_rvoice */
voice->can_access_rvoice = 1;
voice->can_access_overflow_rvoice = 1;
voice->can_access_rvoice = TRUE;
voice->can_access_overflow_rvoice = TRUE;
fluid_voice_initialize_rvoice(voice);
fluid_voice_swap_rvoice(voice);
fluid_voice_initialize_rvoice(voice);
@ -442,8 +450,7 @@ fluid_voice_calculate_gain_amplitude(const fluid_voice_t* voice, fluid_real_t ga
/* we use 24bit samples in fluid_rvoice_dsp. in order to normalize float
* samples to [0.0;1.0] divide samples by the max. value of an int24 and
* amplify them with the gain */
const fluid_real_t INT24_MAX = (1 << (16+8-1)) * 1.0f;
return gain * voice->synth_gain / INT24_MAX;
return gain * voice->synth_gain / (INT24_MAX * 1.0f);
}
void
@ -538,7 +545,9 @@ fluid_voice_calculate_runtime_synthesis_parameters(fluid_voice_t* voice)
/* GEN_FINETUNE [1] #52 */
GEN_OVERRIDEROOTKEY, /* #58 */
GEN_PITCH, /* --- */
GEN_CUSTOM_BALANCE /* --- */
GEN_CUSTOM_BALANCE, /* --- */
GEN_CUSTOM_FILTERFC, /* --- */
GEN_CUSTOM_FILTERQ /* --- */
};
/* When the voice is made ready for the synthesis process, a lot of
@ -682,13 +691,9 @@ calculate_hold_decay_buffers(fluid_voice_t* voice, int gen_base,
void
fluid_voice_update_param(fluid_voice_t* voice, int gen)
{
// Alternate attenuation scale used by EMU10K1 cards when setting the attenuation at the preset or instrument level within the SoundFont bank.
static const float ALT_ATTENUATION_SCALE = 0.4f;
unsigned int count, z;
fluid_real_t q_dB;
fluid_real_t x = fluid_voice_gen_value(voice, gen);
switch (gen) {
case GEN_PAN:
@ -709,8 +714,7 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
break;
case GEN_ATTENUATION:
voice->attenuation = ((fluid_real_t)(voice)->gen[GEN_ATTENUATION].val*ALT_ATTENUATION_SCALE) +
(fluid_real_t)(voice)->gen[GEN_ATTENUATION].mod + (fluid_real_t)(voice)->gen[GEN_ATTENUATION].nrpn;
voice->attenuation = x;
/* Range: SF2.01 section 8.1.3 # 48
* Motivation for range checking:
@ -783,33 +787,18 @@ fluid_voice_update_param(fluid_voice_t* voice, int gen)
break;
case GEN_FILTERQ:
/* The generator contains 'centibels' (1/10 dB) => divide by 10 to
* obtain dB */
q_dB = x / 10.0f;
/* Range: SF2.01 section 8.1.3 # 8 (convert from cB to dB => /10) */
fluid_clip(q_dB, 0.0f, 96.0f);
/* Short version: Modify the Q definition in a way, that a Q of 0
* dB leads to no resonance hump in the freq. response.
*
* Long version: From SF2.01, page 39, item 9 (initialFilterQ):
* "The gain at the cutoff frequency may be less than zero when
* zero is specified". Assume q_dB=0 / q_lin=1: If we would leave
* q as it is, then this results in a 3 dB hump slightly below
* fc. At fc, the gain is exactly the DC gain (0 dB). What is
* (probably) meant here is that the filter does not show a
* resonance hump for q_dB=0. In this case, the corresponding
* q_lin is 1/sqrt(2)=0.707. The filter should have 3 dB of
* attenuation at fc now. In this case Q_dB is the height of the
* resonance peak not over the DC gain, but over the frequency
* response of a non-resonant filter. This idea is implemented as
* follows: */
q_dB -= 3.01f;
UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q_dB, q_dB);
UPDATE_RVOICE_FILTER1(fluid_iir_filter_set_q, x);
break;
/* same as the two above, only for the custom filter */
case GEN_CUSTOM_FILTERFC:
UPDATE_RVOICE_CUSTOM_FILTER1(fluid_iir_filter_set_fres, x);
break;
case GEN_CUSTOM_FILTERQ:
UPDATE_RVOICE_CUSTOM_FILTER1(fluid_iir_filter_set_q, x);
break;
case GEN_MODLFOTOPITCH:
fluid_clip(x, -12000.0, 12000.0);
UPDATE_RVOICE_R1(fluid_rvoice_set_modlfo_to_pitch, x);
@ -1647,20 +1636,21 @@ int fluid_voice_set_gain(fluid_voice_t* voice, fluid_real_t gain)
int
fluid_voice_optimize_sample(fluid_sample_t* s)
{
signed short peak_max = 0;
signed short peak_min = 0;
signed short peak;
int32_t peak_max = 0;
int32_t peak_min = 0;
int32_t peak;
fluid_real_t normalized_amplitude_during_loop;
double result;
int i;
unsigned int i;
/* ignore ROM and other(?) invalid samples */
if (!s->valid) return (FLUID_OK);
if (!s->amplitude_that_reaches_noise_floor_is_valid) { /* Only once */
/* Scan the loop */
for (i = (int)s->loopstart; i < (int)s->loopend; i++){
signed short val = s->data[i];
for (i = s->loopstart; i < s->loopend; i++){
int32_t val = fluid_rvoice_get_sample(s->data, s->data24, i);
if (val > peak_max) {
peak_max = val;
} else if (val < peak_min) {
@ -1687,7 +1677,7 @@ fluid_voice_optimize_sample(fluid_sample_t* s)
*/
/* 16 bits => 96+4=100 dB dynamic range => 0.00001 */
normalized_amplitude_during_loop = ((fluid_real_t)peak)/32768.;
normalized_amplitude_during_loop = ((fluid_real_t)peak)/ (INT24_MAX * 1.0f);
result = FLUID_NOISE_FLOOR / normalized_amplitude_during_loop;
/* Store in sample */
@ -1764,3 +1754,10 @@ fluid_voice_get_overflow_prio(fluid_voice_t* voice,
return this_voice_prio;
}
void fluid_voice_set_custom_filter(fluid_voice_t* voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags)
{
UPDATE_RVOICE_CUSTOM_FILTER_I2(fluid_iir_filter_init, type, flags);
}

View File

@ -185,6 +185,7 @@ fluid_voice_unlock_rvoice(fluid_voice_t* voice)
fluid_real_t fluid_voice_gen_value(const fluid_voice_t* voice, int num);
void fluid_voice_set_custom_filter(fluid_voice_t* voice, enum fluid_iir_filter_type type, enum fluid_iir_filter_flags flags);
#define fluid_voice_get_loudness(voice) (fluid_adsr_env_get_max_val(&voice->volenv))

View File

@ -21,17 +21,9 @@
#include "fluid_conv.h"
/* EMU 8k/10k don't follow spec in regards to volume attenuation.
* This factor is used in the equation pow (10.0, cb / FLUID_ATTEN_POWER_FACTOR).
* By the standard this should be -200.0. */
/* 07/11/2008 modified by S. Christian Collins for increased velocity sensitivity.
* Now it equals the response of EMU10K1 programming.*/
static double FLUID_ATTEN_POWER_FACTOR = FLUID_ATTEN_POWER_DEFAULT_EMU; /* was (-531.509)*/
/* conversion tables */
fluid_real_t fluid_ct2hz_tab[FLUID_CENTS_HZ_SIZE];
fluid_real_t fluid_cb2amp_tab[FLUID_CB_AMP_SIZE];
fluid_real_t fluid_atten2amp_tab[FLUID_ATTEN_AMP_SIZE];
fluid_real_t fluid_concave_tab[128];
fluid_real_t fluid_convex_tab[128];
fluid_real_t fluid_pan_tab[FLUID_PAN_SIZE];
@ -60,16 +52,6 @@ fluid_conversion_config(void)
fluid_cb2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / -200.0);
}
/* NOTE: EMU8k and EMU10k devices don't conform to the SoundFont
* specification in regards to volume attenuation. The below calculation
* is an approx. equation for generating a table equivelant to the
* cb_to_amp_table[] in tables.c of the TiMidity++ source, which I'm told
* was generated from device testing. By the spec this should be centibels.
*/
for (i = 0; i < FLUID_ATTEN_AMP_SIZE; i++) {
fluid_atten2amp_tab[i] = (fluid_real_t) pow(10.0, (double) i / FLUID_ATTEN_POWER_FACTOR);
}
/* initialize the conversion tables (see fluid_mod.c
fluid_mod_get_value cases 4 and 8) */
@ -97,11 +79,6 @@ fluid_conversion_config(void)
}
}
void fluid_conversion_set_atten_power(double atten)
{
FLUID_ATTEN_POWER_FACTOR = atten;
}
/*
* fluid_ct2hz
*/
@ -157,7 +134,7 @@ fluid_ct2hz(fluid_real_t cents)
/*
* fluid_cb2amp
*
* in: a value between 0 and 960, 0 is no attenuation
* in: a value between 0 and 1440, 0 is no attenuation
* out: a value between 1 and 0
*/
fluid_real_t
@ -179,23 +156,6 @@ fluid_cb2amp(fluid_real_t cb)
return fluid_cb2amp_tab[(int) cb];
}
/*
* fluid_atten2amp
*
* in: a value between 0 and 1440, 0 is no attenuation
* out: a value between 1 and 0
*
* Note: Volume attenuation is supposed to be centibels but EMU8k/10k don't
* follow this. Thats the reason for separate fluid_cb2amp and fluid_atten2amp.
*/
fluid_real_t
fluid_atten2amp(fluid_real_t atten)
{
if (atten < 0) return 1.0;
else if (atten >= FLUID_ATTEN_AMP_SIZE) return 0.0;
else return fluid_atten2amp_tab[(int) atten];
}
/*
* fluid_tc2sec_delay
*/

View File

@ -25,20 +25,14 @@
#define FLUID_CENTS_HZ_SIZE 1200
#define FLUID_VEL_CB_SIZE 128
#define FLUID_CB_AMP_SIZE 961
#define FLUID_ATTEN_AMP_SIZE 1441
#define FLUID_CB_AMP_SIZE 1441
#define FLUID_PAN_SIZE 1002
#define FLUID_ATTEN_POWER_DEFAULT_EMU (-200.0)
#define FLUID_ATTEN_POWER_DEFAULT_COMPLIANT (-531.509)
void fluid_conversion_config(void);
void fluid_conversion_set_atten_power(double atten);
fluid_real_t fluid_ct2hz_real(fluid_real_t cents);
fluid_real_t fluid_ct2hz(fluid_real_t cents);
fluid_real_t fluid_cb2amp(fluid_real_t cb);
fluid_real_t fluid_atten2amp(fluid_real_t atten);
fluid_real_t fluid_tc2sec(fluid_real_t tc);
fluid_real_t fluid_tc2sec_delay(fluid_real_t tc);
fluid_real_t fluid_tc2sec_attack(fluid_real_t tc);

View File

@ -1048,17 +1048,18 @@ fluid_settings_str_equal (fluid_settings_t* settings, const char *name, const ch
*
* @param settings a settings object
* @param name a setting's name
* @return the default string value of the setting if it exists, NULL otherwise
* @param def the default string value of the setting if it exists
* @return FLUID_OK on success, FLUID_FAILED otherwise
*/
char*
fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
int
fluid_settings_getstr_default(fluid_settings_t* settings, const char *name, char** def)
{
fluid_setting_node_t *node;
char *retval = NULL;
fluid_return_val_if_fail (settings != NULL, NULL);
fluid_return_val_if_fail (name != NULL, NULL);
fluid_return_val_if_fail (name[0] != '\0', NULL);
fluid_return_val_if_fail (settings != NULL, FLUID_FAILED);
fluid_return_val_if_fail (name != NULL, FLUID_FAILED);
fluid_return_val_if_fail (name[0] != '\0', FLUID_FAILED);
fluid_rec_mutex_lock (settings->mutex);
@ -1078,9 +1079,10 @@ fluid_settings_getstr_default(fluid_settings_t* settings, const char *name)
}
}
*def = retval;
fluid_rec_mutex_unlock (settings->mutex);
return retval;
return retval != NULL ? FLUID_OK : FLUID_FAILED;
}
/**

View File

@ -315,7 +315,7 @@ fluid_is_midifile(const char *filename)
}
fclose(fp);
return strncmp(id, "MThd", 4) == 0;
return FLUID_STRNCMP(id, "MThd", 4) == 0;
}
/**
@ -323,25 +323,32 @@ fluid_is_midifile(const char *filename)
* @param filename Path to the file to check
* @return TRUE if it could be a SoundFont, FALSE otherwise
*
* The current implementation only checks for the "RIFF" header in the file.
* It is useful only to distinguish between SoundFont and MIDI files.
* @note The current implementation only checks for the "RIFF" and "sfbk" headers in
* the file. It is useful to distinguish between SoundFont and other (e.g. MIDI) files.
*/
int
fluid_is_soundfont(const char *filename)
{
FILE* fp = fopen(filename, "rb");
char id[4];
char riff_id[4], sfbk_id[4];
if (fp == NULL) {
return 0;
}
if (fread((void*) id, 1, 4, fp) != 4) {
if((fread((void*) riff_id, 1, sizeof(riff_id), fp) != sizeof(riff_id)) ||
(fseek(fp, 4, SEEK_CUR) != 0) ||
(fread((void*) sfbk_id, 1, sizeof(sfbk_id), fp) != sizeof(sfbk_id)))
{
goto error_rec;
}
fclose(fp);
return (FLUID_STRNCMP(riff_id, "RIFF", sizeof(riff_id)) == 0) &&
(FLUID_STRNCMP(sfbk_id, "sfbk", sizeof(sfbk_id)) == 0);
error_rec:
fclose(fp);
return 0;
}
fclose(fp);
return strncmp(id, "RIFF", 4) == 0;
}
/**

View File

@ -64,6 +64,7 @@ void fluid_time_config(void);
#define FLUID_POINTER_TO_INT GPOINTER_TO_INT
#define FLUID_INT_TO_POINTER GINT_TO_POINTER
#define FLUID_N_ELEMENTS(struct) (sizeof (struct) / sizeof (struct[0]))
#define FLUID_MEMBER_SIZE(struct, member) ( sizeof (((struct *)0)->member) )
#define FLUID_IS_BIG_ENDIAN (G_BYTE_ORDER == G_BIG_ENDIAN)