Merge branch '2.1-testing' into master

This commit is contained in:
derselbst 2018-12-21 20:05:32 +01:00
commit 10ddcc0cd3
20 changed files with 2738 additions and 637 deletions

View File

@ -68,7 +68,9 @@ option ( enable-midishare "compile MidiShare support (if it is available)" on )
option ( enable-network "enable network support (requires BSD sockets)" on )
option ( enable-oss "compile OSS support (if it is available)" on )
option ( enable-dsound "compile DirectSound support (if it is available)" on )
option ( enable-waveout "compile Windows WaveOut support (if it is available)" on )
option ( enable-winmidi "compile Windows MIDI support (if it is available)" on )
option ( enable-sdl2 "compile SDL2 audio support (if it is available)" on )
option ( enable-pkgconfig "use pkg-config to locate fluidsynth's (mostly optional) dependencies" on )
option ( enable-pulseaudio "compile PulseAudio support (if it is available)" on )
option ( enable-readline "compile readline lib line editing (if it is available)" on )
@ -194,6 +196,7 @@ endif ( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE
unset ( WINDOWS_SUPPORT CACHE )
unset ( WINDOWS_LIBS CACHE )
unset ( DSOUND_SUPPORT CACHE )
unset ( WAVEOUT_SUPPORT CACHE )
unset ( WINMIDI_SUPPORT CACHE )
unset ( MINGW32 CACHE )
if ( WIN32 )
@ -220,6 +223,11 @@ if ( WIN32 )
set ( WINMIDI_SUPPORT 1 )
endif ()
if ( enable-waveout AND HAVE_MMSYSTEM_H )
set ( WINDOWS_LIBS "${WINDOWS_LIBS};winmm" )
set ( WAVEOUT_SUPPORT 1 )
endif ()
set ( LIBFLUID_CPPFLAGS "-DFLUIDSYNTH_DLL_EXPORTS" )
set ( FLUID_CPPFLAGS "-DFLUIDSYNTH_NOT_A_DLL" )
if ( MSVC )
@ -547,6 +555,20 @@ if ( enable-oss )
set ( OSS_SUPPORT ${OSS_FOUND} )
endif ( enable-oss )
unset ( SDL2_SUPPORT CACHE )
unset ( SDL2_INCLUDE_DIR CACHE )
unset ( SDL2_LIBRARY CACHE )
if ( enable-sdl2 )
find_package ( SDL2 )
if ( SDL2_FOUND )
set ( SDL2_SUPPORT ${SDL2_FOUND} )
else ( SDL2_FOUND)
unset ( SDL2_INCLUDE_DIR CACHE )
unset ( SDL2_LIBRARY CACHE )
endif ( SDL2_FOUND )
endif ( enable-sdl2 )
unset ( MIDISHARE_SUPPORT CACHE )
if ( enable-midishare )
find_package ( MidiShare QUIET )

164
cmake_admin/FindSDL2.cmake Normal file
View File

@ -0,0 +1,164 @@
# Locate SDL2 library
# This module defines
# SDL2_LIBRARY, the name of the library to link against
# SDL2_FOUND, if false, do not try to link to SDL2
# SDL2_INCLUDE_DIR, where to find SDL.h
#
# This module responds to the the flag:
# SDL2_BUILDING_LIBRARY
# If this is defined, then no SDL2main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL2_LIBRARY variable.
#
# Don't forget to include SDLmain.h and SDLmain.m your project for the
# OS X framework based version. (Other versions link to -lSDL2main which
# this module will try to find on your behalf.) Also for OS X, this
# module will automatically add the -framework Cocoa on your behalf.
#
#
# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration
# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library
# (SDL2.dll, libsdl2.so, SDL2.framework, etc).
# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again.
# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value
# as appropriate. These values are used to generate the final SDL2_LIBRARY
# variable, but when these values are unset, SDL2_LIBRARY does not get created.
#
#
# $SDL2DIR is an environment variable that would
# correspond to the ./configure --prefix=$SDL2DIR
# used in building SDL2.
# l.e.galup 9-20-02
#
# Modified by Eric Wing.
# Added code to assist with automated building by using environmental variables
# and providing a more controlled/consistent search behavior.
# Added new modifications to recognize OS X frameworks and
# additional Unix paths (FreeBSD, etc).
# Also corrected the header search path to follow "proper" SDL guidelines.
# Added a search for SDL2main which is needed by some platforms.
# Added a search for threads which is needed by some platforms.
# Added needed compile switches for MinGW.
#
# On OSX, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# SDL2_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
#
# Note that the header path has changed from SDL2/SDL.h to just SDL.h
# This needed to change because "proper" SDL convention
# is #include "SDL.h", not <SDL2/SDL.h>. This is done for portability
# reasons because not all systems place things in SDL2/ (see FreeBSD).
#=============================================================================
# Copyright 2003-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
SET(SDL2_SEARCH_PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
FIND_PATH(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES include/SDL2 include
PATHS ${SDL2_SEARCH_PATHS}
)
FIND_LIBRARY(SDL2_LIBRARY_TEMP
NAMES SDL2
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
IF(NOT SDL2_BUILDING_LIBRARY)
IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
# Non-OS X framework versions expect you to also dynamically link to
# SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms
# seem to provide SDL2main for compatibility even though they don't
# necessarily need it.
FIND_LIBRARY(SDL2MAIN_LIBRARY
NAMES SDL2main
HINTS
$ENV{SDL2DIR} ${SDL2_ROOT}
PATH_SUFFIXES lib64 lib
PATHS ${SDL2_SEARCH_PATHS}
)
ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework")
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# SDL2 may require threads on your system.
# The Apple build may not need an explicit flag because one of the
# frameworks may already provide it.
# But for non-OSX systems, I will use the CMake Threads package.
IF(NOT APPLE)
FIND_PACKAGE(Threads)
ENDIF(NOT APPLE)
# MinGW needs an additional library, mwindows
# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows
# (Actually on second look, I think it only needs one of the m* libraries.)
IF(MINGW)
SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW")
ENDIF(MINGW)
IF(SDL2_LIBRARY_TEMP)
# For SDL2main
IF(NOT SDL2_BUILDING_LIBRARY)
IF(SDL2MAIN_LIBRARY)
SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(SDL2MAIN_LIBRARY)
ENDIF(NOT SDL2_BUILDING_LIBRARY)
# For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa.
# CMake doesn't display the -framework Cocoa string in the UI even
# though it actually is there if I modify a pre-used variable.
# I think it has something to do with the CACHE STRING.
# So I use a temporary variable until the end so I can set the
# "real" variable in one-shot.
IF(APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa")
ENDIF(APPLE)
# For threads, as mentioned Apple doesn't need this.
# In fact, there seems to be a problem if I used the Threads package
# and try using this line, so I'm just skipping it entirely for OS X.
IF(NOT APPLE)
SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT})
ENDIF(NOT APPLE)
# For MinGW library
IF(MINGW)
SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP})
ENDIF(MINGW)
# Set the final string here so the GUI reflects the final state.
SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found")
# Set the temp variable to INTERNAL so it is not seen in the CMake GUI
SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "")
ENDIF(SDL2_LIBRARY_TEMP)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR)

View File

@ -80,12 +80,24 @@ else ( DSOUND_SUPPORT )
message ( "DSound: no" )
endif ( DSOUND_SUPPORT )
if ( WAVEOUT_SUPPORT )
message ( "WaveOut support: yes" )
else ( WAVEOUT_SUPPORT )
message ( "WaveOut support: no" )
endif ( WAVEOUT_SUPPORT )
if ( WINMIDI_SUPPORT )
message ( "WinMidi support: yes" )
else ( WINMIDI_SUPPORT )
message ( "WinMidi support: no" )
endif ( WINMIDI_SUPPORT )
if ( SDL2_SUPPORT )
message ( "SDL2 support: yes" )
else ( SDL2_SUPPORT )
message ( "SDL2 support: no" )
endif ( SDL2_SUPPORT )
if ( LADSPA_SUPPORT )
message ( "LADSPA support: yes" )
else ( LADSPA_SUPPORT )

View File

@ -367,9 +367,9 @@ Developers: Settings can be deprecated by adding: <deprecated>SOME TEXT</depreca
coreaudio (Mac OS X),<br />
dart (OS/2)
</def>
<vals>jack, alsa, oss, pulseaudio, coreaudio, dsound, portaudio, sndman, dart, file</vals>
<vals>alsa, coreaudio, dart, dsound, file, jack, oss, portaudio, pulseaudio, sdl2, sndman, waveout</vals>
<desc>
The audio system to be used.
The audio system to be used. In order to use sdl2 as audio driver, the application is responsible for initializing SDL's audio subsystem.
</desc>
</setting>
<setting>

View File

@ -206,7 +206,7 @@ For a full list of available <strong>synthesizer settings</strong>, please refer
The synthesizer itself does not write any audio to the audio output. This allows application developers to manage the audio output themselves if they wish. The next section describes the use of the synthesizer without an audio driver in more detail.
Creating the audio driver is straightforward: set the appropriate settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description.
Creating the audio driver is straightforward: set the <code>audio.driver</code> settings and create the driver object. Because the FluidSynth has support for several audio systems, you may want to change which one you want to use. The list below shows the audio systems that are currently supported. It displays the name, as used by the fluidsynth library, and a description.
- jack: JACK Audio Connection Kit (Linux, Mac OS X, Windows)
- alsa: Advanced Linux Sound Architecture (Linux)
@ -218,6 +218,7 @@ Creating the audio driver is straightforward: set the appropriate settings and c
- sndman: Apple SoundManager (Mac OS Classic)
- dart: DART sound driver (OS/2)
- file: Driver to output audio to a file
- sdl2*: Simple DirectMedia Layer (Linux, Windows, Mac OS X, iOS, Android, FreeBSD, Haiku, etc.)
The default audio driver depends on the settings with which FluidSynth was compiled. You can get the default driver with fluid_settings_getstr_default(). To get the list of available drivers use the fluid_settings_foreach_option() function. Finally, you can set the driver with fluid_settings_setstr(). In most cases, the default driver should work out of the box.
@ -245,7 +246,7 @@ As soon as the audio driver is created, it will start playing. The audio driver
There are a number of general audio driver settings. The audio.driver settings define the audio subsystem that will be used. The audio.periods and audio.period-size settings define the latency and robustness against scheduling delays. There are additional settings for the audio subsystems used. For a full list of available <strong>audio driver settings</strong>, please refer to <a href="fluidsettings.xml" target="_blank"><strong>FluidSettings Documentation</strong></a>.
<strong>*Note:</strong> In order to use sdl2 as audio driver, the application is responsible for initializing SDL (e.g. with SDL_Init()). This must be done <strong>before</strong> the first call to <code>new_fluid_settings()</code>! Also make sure to call SDL_Quit() after all fluidsynth instances have been destroyed.
\section UsingSynth Using the synthesizer without an audio driver

View File

@ -32,6 +32,7 @@ include_directories (
${CMAKE_SOURCE_DIR}/include
${CMAKE_BINARY_DIR}/include
${PTHREADS_INCLUDE_DIR}
${SDL2_INCLUDE_DIR}
)
include_directories (
@ -82,10 +83,18 @@ if ( DSOUND_SUPPORT )
set ( fluid_dsound_SOURCES drivers/fluid_dsound.c )
endif ( DSOUND_SUPPORT )
if ( WAVEOUT_SUPPORT )
set ( fluid_waveout_SOURCES drivers/fluid_waveout.c )
endif ( WAVEOUT_SUPPORT )
if ( WINMIDI_SUPPORT )
set ( fluid_winmidi_SOURCES drivers/fluid_winmidi.c )
endif ( WINMIDI_SUPPORT )
if ( SDL2_SUPPORT )
set ( fluid_sdl2_SOURCES drivers/fluid_sdl2.c )
endif ( SDL2_SUPPORT )
if ( OSS_SUPPORT )
set ( fluid_oss_SOURCES drivers/fluid_oss.c )
endif ( OSS_SUPPORT )
@ -248,7 +257,9 @@ add_library ( libfluidsynth-OBJ OBJECT
${fluid_portaudio_SOURCES}
${fluid_pulse_SOURCES}
${fluid_dsound_SOURCES}
${fluid_waveout_SOURCES}
${fluid_winmidi_SOURCES}
${fluid_sdl2_SOURCES}
${libfluidsynth_SOURCES}
${public_HEADERS}
${public_main_HEADER}
@ -316,6 +327,7 @@ target_link_libraries ( libfluidsynth
${PULSE_LIBRARIES}
${PORTAUDIO_LIBRARIES}
${LIBSNDFILE_LIBRARIES}
${SDL2_LIBRARY}
${DBUS_LIBRARIES}
${READLINE_LIBS}
${DART_LIBS}

View File

@ -193,9 +193,15 @@
/* Define to enable DirectSound driver */
#cmakedefine DSOUND_SUPPORT @DSOUND_SUPPORT@
/* Define to enable Windows WaveOut driver */
#cmakedefine WAVEOUT_SUPPORT @WAVEOUT_SUPPORT@
/* Define to enable Windows MIDI driver */
#cmakedefine WINMIDI_SUPPORT @WINMIDI_SUPPORT@
/* Define to enable SDL2 audio driver */
#cmakedefine SDL2_SUPPORT @SDL2_SUPPORT@
/* Define to 1 if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS @STDC_HEADERS@

View File

@ -99,6 +99,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if WAVEOUT_SUPPORT
{
"waveout",
new_fluid_waveout_audio_driver,
NULL,
delete_fluid_waveout_audio_driver,
fluid_waveout_audio_driver_settings
},
#endif
#if SNDMAN_SUPPORT
{
"sndman",
@ -129,6 +139,16 @@ static const fluid_audriver_definition_t fluid_audio_drivers[] =
},
#endif
#if SDL2_SUPPORT
{
"sdl2",
new_fluid_sdl2_audio_driver,
NULL,
delete_fluid_sdl2_audio_driver,
fluid_sdl2_audio_driver_settings
},
#endif
#if AUFILE_SUPPORT
{
"file",

View File

@ -83,6 +83,13 @@ void delete_fluid_dsound_audio_driver(fluid_audio_driver_t *p);
void fluid_dsound_audio_driver_settings(fluid_settings_t *settings);
#endif
#if WAVEOUT_SUPPORT
fluid_audio_driver_t *new_fluid_waveout_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);
void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *p);
void fluid_waveout_audio_driver_settings(fluid_settings_t *settings);
#endif
#if PORTAUDIO_SUPPORT
void fluid_portaudio_driver_settings(fluid_settings_t *settings);
fluid_audio_driver_t *new_fluid_portaudio_driver(fluid_settings_t *settings,
@ -114,6 +121,13 @@ void delete_fluid_dart_audio_driver(fluid_audio_driver_t *p);
void fluid_dart_audio_driver_settings(fluid_settings_t *settings);
#endif
#if SDL2_SUPPORT
fluid_audio_driver_t *new_fluid_sdl2_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);
void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *p);
void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings);
#endif
#if AUFILE_SUPPORT
fluid_audio_driver_t *new_fluid_file_audio_driver(fluid_settings_t *settings,
fluid_synth_t *synth);

View File

@ -29,6 +29,8 @@
#include "fluid_settings.h"
#if AUFILE_SUPPORT
/** fluid_file_audio_driver_t
*
* This structure should not be accessed directly. Use audio port
@ -129,3 +131,5 @@ static int fluid_file_audio_run_s16(void *d, unsigned int clock_time)
return fluid_file_renderer_process_block(dev->renderer) == FLUID_OK ? 1 : 0;
}
#endif /* AUFILE_SUPPORT */

251
src/drivers/fluid_sdl2.c Normal file
View File

@ -0,0 +1,251 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
* Copyright (C) 2018 Carlo Bramini
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "fluid_synth.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#if SDL2_SUPPORT
#include "SDL.h"
typedef struct
{
fluid_audio_driver_t driver;
fluid_synth_t *synth;
fluid_audio_callback_t write_ptr;
SDL_AudioDeviceID devid;
int frame_size;
} fluid_sdl2_audio_driver_t;
static void
SDLAudioCallback(void *data, void *stream, int len)
{
fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *)data;
len /= dev->frame_size;
dev->write_ptr(dev->synth, len, stream, 0, 2, stream, 1, 2);
}
void fluid_sdl2_audio_driver_settings(fluid_settings_t *settings)
{
int n, nDevs;
fluid_settings_register_str(settings, "audio.sdl2.device", "default", 0);
fluid_settings_add_option(settings, "audio.sdl2.device", "default");
if(!SDL_WasInit(SDL_INIT_AUDIO))
{
FLUID_LOG(FLUID_ERR, "SDL2 not initialized");
return;
}
nDevs = SDL_GetNumAudioDevices(0);
for(n = 0; n < nDevs; n++)
{
const char *dev_name = SDL_GetAudioDeviceName(n, 0);
if(dev_name != NULL)
{
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name);
fluid_settings_add_option(settings, "audio.sdl2.device", dev_name);
}
}
}
/*
* new_fluid_sdl2_audio_driver
*/
fluid_audio_driver_t *
new_fluid_sdl2_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
fluid_sdl2_audio_driver_t *dev = NULL;
fluid_audio_callback_t write_ptr;
double sample_rate;
int period_size, sample_size;
SDL_AudioSpec aspec, rspec;
char *device;
const char *dev_name;
/* Retrieve the settings */
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.period-size", &period_size);
/* Lower values do not seem to give good results */
if(period_size < 1024)
{
period_size = 1024;
}
else
/* According to documentation, it MUST be a power of two */
if((period_size & (period_size - 1)) != 0)
{
FLUID_LOG(FLUID_DBG, "\"audio.period-size\" must be a power of 2");
return NULL;
}
/* Clear the format buffer */
FLUID_MEMSET(&aspec, 0, sizeof(aspec));
/* Setup mixing frequency */
aspec.freq = (int)sample_rate;
/* Check the format */
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
{
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
sample_size = sizeof(float);
write_ptr = fluid_synth_write_float;
aspec.format = AUDIO_F32SYS;
}
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
{
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
sample_size = sizeof(short);
write_ptr = fluid_synth_write_s16;
aspec.format = AUDIO_S16SYS;
}
else
{
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
return NULL;
}
/* Compile the format buffer */
aspec.channels = 2;
aspec.samples = aspec.channels * ((period_size + 7) & ~7);
aspec.callback = (SDL_AudioCallback)SDLAudioCallback;
/* Check if SDL library has been started */
if(!SDL_WasInit(SDL_INIT_AUDIO))
{
FLUID_LOG(FLUID_ERR, "SDL2 not initialized");
return NULL;
}
/* Set default device to use */
device = NULL;
dev_name = NULL;
/* get the selected device name. if none is specified, use default device. */
if(fluid_settings_dupstr(settings, "audio.sdl2.device", &device) == FLUID_OK
&& device != NULL && device[0] != '\0')
{
int n, nDevs = SDL_GetNumAudioDevices(0);
for(n = 0; n < nDevs; n++)
{
dev_name = SDL_GetAudioDeviceName(n, 0);
if(FLUID_STRCASECMP(dev_name, device) == 0)
{
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name);
break;
}
}
if(n >= nDevs)
{
FLUID_LOG(FLUID_DBG, "Audio device %s, using \"default\"", device);
dev_name = NULL;
}
}
if(device != NULL)
{
FLUID_FREE(device);
}
do
{
/* create and clear the driver data */
dev = FLUID_NEW(fluid_sdl2_audio_driver_t);
if(dev == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
break;
}
FLUID_MEMSET(dev, 0, sizeof(fluid_sdl2_audio_driver_t));
/* set device pointer to userdata */
aspec.userdata = dev;
/* Save copy of synth */
dev->synth = synth;
/* Save copy of other variables */
dev->write_ptr = write_ptr;
dev->frame_size = sample_size * aspec.channels;
/* Open audio device */
dev->devid = SDL_OpenAudioDevice(dev_name, 0, &aspec, &rspec, 0);
if(!dev->devid)
{
FLUID_LOG(FLUID_ERR, "Failed to open audio device");
break;
}
/* Start to play */
SDL_PauseAudioDevice(dev->devid, 0);
return (fluid_audio_driver_t *) dev;
}
while(0);
delete_fluid_sdl2_audio_driver(&dev->driver);
return NULL;
}
void delete_fluid_sdl2_audio_driver(fluid_audio_driver_t *d)
{
fluid_sdl2_audio_driver_t *dev = (fluid_sdl2_audio_driver_t *) d;
if(dev != NULL)
{
if(dev->devid)
{
/* Stop audio and close */
SDL_PauseAudioDevice(dev->devid, 1);
SDL_CloseAudioDevice(dev->devid);
}
FLUID_FREE(dev);
}
}
#endif /* SDL2_SUPPORT */

386
src/drivers/fluid_waveout.c Normal file
View File

@ -0,0 +1,386 @@
/* FluidSynth - A Software Synthesizer
*
* Copyright (C) 2003 Peter Hanappe and others.
* Copyright (C) 2018 Carlo Bramini
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA
*/
#include "fluid_synth.h"
#include "fluid_adriver.h"
#include "fluid_settings.h"
#if WAVEOUT_SUPPORT
#include <mmsystem.h>
#define NOBITMAP
#include <mmreg.h>
/* Number of buffers in the chain */
#define NB_SOUND_BUFFERS 4
/* Milliseconds of a single sound buffer */
#define MS_BUFFER_LENGTH 20
typedef struct
{
fluid_audio_driver_t driver;
fluid_synth_t *synth;
fluid_audio_callback_t write_ptr;
HWAVEOUT hWaveOut;
WAVEHDR waveHeader[NB_SOUND_BUFFERS];
int sample_size;
int num_frames;
HANDLE hThread;
DWORD dwThread;
int nQuit;
HANDLE hQuit;
} fluid_waveout_audio_driver_t;
/* Thread for playing sample buffers */
static DWORD WINAPI fluid_waveout_synth_thread(void *data)
{
fluid_waveout_audio_driver_t *dev;
WAVEHDR *pWave;
MSG msg;
int code;
/* Forces creation of message queue */
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
for(;;)
{
code = GetMessage(&msg, NULL, 0, 0);
if(code < 0)
{
FLUID_LOG(FLUID_ERR, "fluid_waveout_synth_thread: GetMessage() failed.");
break;
}
if(msg.message == WM_CLOSE)
{
break;
}
switch(msg.message)
{
case MM_WOM_DONE:
pWave = (WAVEHDR *)msg.lParam;
dev = (fluid_waveout_audio_driver_t *)pWave->dwUser;
if(dev->nQuit > 0)
{
/* Release the sample buffer */
waveOutUnprepareHeader((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR));
if(--dev->nQuit == 0)
{
SetEvent(dev->hQuit);
}
}
else
{
dev->write_ptr(dev->synth, dev->num_frames, pWave->lpData, 0, 2, pWave->lpData, 1, 2);
waveOutWrite((HWAVEOUT)msg.wParam, pWave, sizeof(WAVEHDR));
}
break;
}
}
return 0;
}
void fluid_waveout_audio_driver_settings(fluid_settings_t *settings)
{
UINT n, nDevs = waveOutGetNumDevs();
#ifdef _UNICODE
char dev_name[MAXPNAMELEN];
#endif
fluid_settings_register_str(settings, "audio.waveout.device", "default", 0);
fluid_settings_add_option(settings, "audio.waveout.device", "default");
for(n = 0; n < nDevs; n++)
{
WAVEOUTCAPS caps;
MMRESULT res;
res = waveOutGetDevCaps(n, &caps, sizeof(caps));
if(res == MMSYSERR_NOERROR)
{
#ifdef _UNICODE
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, dev_name, MAXPNAMELEN, 0, 0);
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", dev_name);
fluid_settings_add_option(settings, "audio.waveout.device", dev_name);
#else
FLUID_LOG(FLUID_DBG, "Testing audio device: %s", caps.szPname);
fluid_settings_add_option(settings, "audio.waveout.device", caps.szPname);
#endif
}
}
}
/*
* new_fluid_waveout_audio_driver
*/
fluid_audio_driver_t *
new_fluid_waveout_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
{
fluid_waveout_audio_driver_t *dev = NULL;
fluid_audio_callback_t write_ptr;
double sample_rate;
int periods, period_size, frequency, sample_size;
LPSTR ptrBuffer;
int lenBuffer;
int device;
int i;
WAVEFORMATEX wfx;
char dev_name[MAXPNAMELEN];
MMRESULT errCode;
/* Retrieve the settings */
fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
fluid_settings_getint(settings, "audio.periods", &periods);
fluid_settings_getint(settings, "audio.period-size", &period_size);
/* Clear the format buffer */
ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
/* check the format */
if(fluid_settings_str_equal(settings, "audio.sample-format", "float"))
{
FLUID_LOG(FLUID_DBG, "Selected 32 bit sample format");
sample_size = sizeof(float);
write_ptr = fluid_synth_write_float;
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
}
else if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
{
FLUID_LOG(FLUID_DBG, "Selected 16 bit sample format");
sample_size = sizeof(short);
write_ptr = fluid_synth_write_s16;
wfx.wFormatTag = WAVE_FORMAT_PCM;
}
else
{
FLUID_LOG(FLUID_ERR, "Unhandled sample format");
return NULL;
}
/* Set frequency to integer */
frequency = (int)sample_rate;
/* Compile the format buffer */
wfx.nChannels = 2;
wfx.wBitsPerSample = sample_size * 8;
wfx.nSamplesPerSec = frequency;
wfx.nBlockAlign = sample_size * wfx.nChannels;
wfx.nAvgBytesPerSec = frequency * wfx.nBlockAlign;
/* Calculate the length of a single buffer */
lenBuffer = (MS_BUFFER_LENGTH * wfx.nAvgBytesPerSec + 999) / 1000;
/* Round to 8-bytes size */
lenBuffer = (lenBuffer + 7) & ~7;
/* create and clear the driver data */
dev = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(fluid_waveout_audio_driver_t) + lenBuffer * NB_SOUND_BUFFERS);
if(dev == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory");
return NULL;
}
/* Save copy of synth */
dev->synth = synth;
/* Save copy of other variables */
dev->write_ptr = write_ptr;
dev->sample_size = sample_size;
/* Calculate the number of frames in a block */
dev->num_frames = lenBuffer / wfx.nBlockAlign;
/* Set default device to use */
device = WAVE_MAPPER;
/* get the selected device name. if none is specified, use default device. */
if(fluid_settings_copystr(settings, "audio.waveout.device", dev_name, MAXPNAMELEN) == FLUID_OK
&& dev_name[0] != '\0')
{
UINT nDevs = waveOutGetNumDevs();
UINT n;
#ifdef _UNICODE
WCHAR lpwDevName[MAXPNAMELEN];
MultiByteToWideChar(CP_UTF8, 0, dev_name, -1, lpwDevName, MAXPNAMELEN);
#endif
for(n = 0; n < nDevs; n++)
{
WAVEOUTCAPS caps;
MMRESULT res;
res = waveOutGetDevCaps(n, &caps, sizeof(caps));
if(res == MMSYSERR_NOERROR)
{
#ifdef _UNICODE
if(wcsicmp(lpwDevName, caps.szPname) == 0)
#else
if(FLUID_STRCASECMP(dev_name, caps.szPname) == 0)
#endif
{
FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %s", dev_name);
device = n;
break;
}
}
}
}
do
{
dev->hQuit = CreateEvent(NULL, FALSE, FALSE, NULL);
if(dev->hQuit == NULL)
{
FLUID_LOG(FLUID_ERR, "Failed to create quit event");
break;
}
/* Create thread which processes re-adding SYSEX buffers */
dev->hThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)
fluid_waveout_synth_thread,
dev,
0,
&dev->dwThread);
if(dev->hThread == NULL)
{
FLUID_LOG(FLUID_ERR, "Failed to create waveOut thread");
break;
}
errCode = waveOutOpen(&dev->hWaveOut,
device,
&wfx,
(DWORD_PTR)dev->dwThread,
0,
CALLBACK_THREAD);
if(errCode != MMSYSERR_NOERROR)
{
FLUID_LOG(FLUID_ERR, "Failed to open waveOut device");
break;
}
/* Get pointer to sound buffer memory */
ptrBuffer = (LPSTR)(dev + 1);
/* Setup the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
{
/* Clear the sample buffer */
memset(ptrBuffer, 0, lenBuffer);
/* Clear descriptor buffer */
memset(dev->waveHeader + i, 0, sizeof(WAVEHDR));
/* Compile descriptor buffer */
dev->waveHeader[i].lpData = ptrBuffer;
dev->waveHeader[i].dwBufferLength = lenBuffer;
dev->waveHeader[i].dwUser = (DWORD_PTR)dev;
waveOutPrepareHeader(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR));
ptrBuffer += lenBuffer;
}
/* Play the sample buffers */
for(i = 0; i < NB_SOUND_BUFFERS; i++)
{
waveOutWrite(dev->hWaveOut, &dev->waveHeader[i], sizeof(WAVEHDR));
}
return (fluid_audio_driver_t *) dev;
}
while(0);
delete_fluid_waveout_audio_driver(&dev->driver);
return NULL;
}
void delete_fluid_waveout_audio_driver(fluid_audio_driver_t *d)
{
int i;
fluid_waveout_audio_driver_t *dev = (fluid_waveout_audio_driver_t *) d;
fluid_return_if_fail(dev != NULL);
/* release all the allocated resources */
if(dev->hWaveOut != NULL)
{
dev->nQuit = NB_SOUND_BUFFERS;
WaitForSingleObject(dev->hQuit, INFINITE);
waveOutClose(dev->hWaveOut);
}
if(dev->hThread != NULL)
{
PostThreadMessage(dev->dwThread, WM_CLOSE, 0, 0);
WaitForSingleObject(dev->hThread, INFINITE);
CloseHandle(dev->hThread);
}
if(dev->hQuit != NULL)
{
CloseHandle(dev->hQuit);
}
HeapFree(GetProcessHeap(), 0, dev);
}
#endif /* WAVEOUT_SUPPORT */

File diff suppressed because it is too large Load Diff

View File

@ -305,6 +305,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
{
int ticks = voice->envlfo.ticks;
int count, is_looping;
fluid_real_t modenv_val;
/******************* sample sanity check **********/
@ -361,6 +362,12 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
/******************* phase **********************/
/* SF2.04 section 8.1.2 #26:
* attack of modEnv is convex ?!?
*/
modenv_val = (fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
? fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv))
: fluid_adsr_env_get_val(&voice->envlfo.modenv);
/* Calculate the number of samples, that the DSP loop advances
* through the original waveform with each step in the output
* buffer. It is the ratio between the frequencies of original
@ -369,7 +376,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
voice->dsp.pitchoffset +
fluid_lfo_get_val(&voice->envlfo.modlfo) * voice->envlfo.modlfo_to_pitch
+ fluid_lfo_get_val(&voice->envlfo.viblfo) * voice->envlfo.viblfo_to_pitch
+ fluid_adsr_env_get_val(&voice->envlfo.modenv) * voice->envlfo.modenv_to_pitch)
+ modenv_val * voice->envlfo.modenv_to_pitch)
/ voice->dsp.root_pitch_hz;
/******************* portamento ****************/
@ -455,7 +462,7 @@ fluid_rvoice_write(fluid_rvoice_t *voice, fluid_real_t *dsp_buf)
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);
modenv_val * voice->envlfo.modenv_to_fc);
fluid_iir_filter_apply(&voice->resonant_filter, dsp_buf, count);
@ -585,7 +592,7 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
/* A voice is turned off during the attack section of the volume
* envelope. The attack section ramps up linearly with
* amplitude. The other sections use logarithmic scaling. Calculate new
* volenv_val to achieve equievalent amplitude during the release phase
* volenv_val to achieve equivalent amplitude during the release phase
* for seamless volume transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.volenv) > 0)
@ -598,6 +605,24 @@ fluid_rvoice_noteoff_LOCAL(fluid_rvoice_t *voice, unsigned int min_ticks)
}
}
if(fluid_adsr_env_get_section(&voice->envlfo.modenv) == FLUID_VOICE_ENVATTACK)
{
/* A voice is turned off during the attack section of the modulation
* envelope. The attack section use convex scaling with pitch and filter
* frequency cutoff (see fluid_rvoice_write(): modenv_val = fluid_convex(127 * modenv.val)
* The other sections use linear scaling: modenv_val = modenv.val
*
* Calculate new modenv.val to achieve equivalent modenv_val during the release phase
* for seamless pitch and filter frequency cutoff transition.
*/
if(fluid_adsr_env_get_val(&voice->envlfo.modenv) > 0)
{
fluid_real_t env_value = fluid_convex(127 * fluid_adsr_env_get_val(&voice->envlfo.modenv));
fluid_clip(env_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, env_value);
}
}
fluid_adsr_env_set_section(&voice->envlfo.volenv, FLUID_VOICE_ENVRELEASE);
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVRELEASE);
}
@ -650,11 +675,12 @@ static FLUID_INLINE void fluid_rvoice_local_retrigger_attack(fluid_rvoice_t *voi
DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
{
fluid_rvoice_t *voice = obj;
int section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
int section; /* volume or modulation section */
/*-------------------------------------------------------------------------
Section skip for volume envelope
--------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.volenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use logarithmic scaling. Calculates new
@ -672,16 +698,30 @@ DECLARE_FLUID_RVOICE_FUNCTION(fluid_rvoice_multi_retrigger_attack)
/* skips to Attack section from any section */
/* Update vol and attack data */
fluid_rvoice_local_retrigger_attack(voice);
/*-------------------------------------------------------------------------
Section skip for modulation envelope
--------------------------------------------------------------------------*/
section = fluid_adsr_env_get_section(&voice->envlfo.modenv);
if(section >= FLUID_VOICE_ENVHOLD)
{
/* DECAY, SUSTAIN,RELEASE section use linear scaling.
Since v 2.1 , as recommended by soundfont 2.01/2.4 spec, ATTACK section
uses convex shape (see fluid_rvoice_write() - fluid_convex()).
Calculate new modenv value (new_value) for seamless attack transition.
Here we need the inverse of fluid_convex() function defined as:
new_value = pow(10, (1 - current_val) . FLUID_PEAK_ATTENUATION / -200 . 2.0)
For performance reason we use fluid_cb2amp(Val) = pow(10, val/-200) with
val = (1 current_val) . FLUID_PEAK_ATTENUATION / 2.0
*/
fluid_real_t new_value; /* new modenv value */
new_value = fluid_cb2amp((1.0f - fluid_adsr_env_get_val(&voice->envlfo.modenv))
* FLUID_PEAK_ATTENUATION / 2.0);
fluid_clip(new_value, 0.0, 1.0);
fluid_adsr_env_set_val(&voice->envlfo.modenv, new_value);
}
/* Skips from any section to ATTACK section */
fluid_adsr_env_set_section(&voice->envlfo.modenv, FLUID_VOICE_ENVATTACK);
/* Actually (v 1.1.6) all sections are linear, so there is no need to
correct val value. However soundfont 2.01/2.4 spec. says that Attack should
be convex (see issue #153 from Christian Collins). In the case Attack
section would be changed to a non linear shape it will be necessary to do
a correction for seamless val transition. Here is the place to do this */
}
/**

View File

@ -682,6 +682,140 @@ fluid_defpreset_next(fluid_defpreset_t *defpreset)
return defpreset->next;
}
/*
* Adds global and local modulators list to the voice. This is done in 2 steps:
* - Step 1: Local modulators replace identic global modulators.
* - Step 2: global + local modulators are added to the voice using mode.
*
* Instrument zone list (local/global) must be added using FLUID_VOICE_OVERWRITE.
* Preset zone list (local/global) must be added using FLUID_VOICE_ADD.
*
* @param voice voice instance.
* @param global_mod global list of modulators.
* @param local_mod local list of modulators.
* @param mode Determines how to handle an existing identical modulator.
* #FLUID_VOICE_ADD to add (offset) the modulator amounts,
* #FLUID_VOICE_OVERWRITE to replace the modulator,
*/
static void
fluid_defpreset_noteon_add_mod_to_voice(fluid_voice_t *voice,
fluid_mod_t *global_mod, fluid_mod_t *local_mod,
int mode)
{
fluid_mod_t *mod;
/* list for 'sorting' global/local modulators */
fluid_mod_t *mod_list[FLUID_NUM_MOD];
int mod_list_count, i;
/* identity_limit_count is the modulator upper limit number to handle with
* existing identical modulators.
* When identity_limit_count is below the actual number of modulators, this
* will restrict identity check to this upper limit,
* This is useful when we know by advance that there is no duplicate with
* modulators at index above this limit. This avoid wasting cpu cycles at
* noteon.
*/
int identity_limit_count;
/* Step 1: Local modulators replace identic global modulators. */
/* local (instrument zone/preset zone), modulators: Put them all into a list. */
mod_list_count = 0;
while(local_mod)
{
/* As modulators number in local_mod list was limited to FLUID_NUM_MOD at
soundfont loading time (fluid_limit_mod_list()), here we don't need
to check if mod_list is full.
*/
mod_list[mod_list_count++] = local_mod;
local_mod = local_mod->next;
}
/* global (instrument zone/preset zone), modulators.
* Replace modulators with the same definition in the global list:
* (Instrument zone: SF 2.01 page 69, 'bullet' 8)
* (Preset zone: SF 2.01 page 69, second-last bullet).
*
* mod_list contains local modulators. Now we know that there
* is no global modulator identic to another global modulator (this has
* been checked at soundfont loading time). So global modulators
* are only checked against local modulators number.
*/
/* Restrict identity check to the number of local modulators */
identity_limit_count = mod_list_count;
while(global_mod)
{
/* 'Identical' global modulators are ignored.
* SF2.01 section 9.5.1
* page 69, 'bullet' 3 defines 'identical'. */
for(i = 0; i < identity_limit_count; i++)
{
if(fluid_mod_test_identity(global_mod, mod_list[i]))
{
break;
}
}
/* Finally add the new modulator to the list. */
if(i >= identity_limit_count)
{
/* Although local_mod and global_mod lists was limited to
FLUID_NUM_MOD at soundfont loading time, it is possible that
local + global modulators exceeds FLUID_NUM_MOD.
So, checks if mod_list_count reachs the limit.
*/
if(mod_list_count >= FLUID_NUM_MOD)
{
/* mod_list is full, we silently forget this modulator and
next global modulators. When mod_list will be added to the
voice, a warning will be displayed if the voice list is full.
(see fluid_voice_add_mod_local()).
*/
break;
}
mod_list[mod_list_count++] = global_mod;
}
global_mod = global_mod->next;
}
/* Step 2: global + local modulators are added to the voice using mode. */
/*
* mod_list contains local and global modulators, we know that:
* - there is no global modulator identic to another global modulator,
* - there is no local modulator identic to another local modulator,
* So these local/global modulators are only checked against
* actual number of voice modulators.
*/
/* Restrict identity check to the actual number of voice modulators */
/* Acual number of voice modulators : defaults + [instruments] */
identity_limit_count = voice->mod_count;
for(i = 0; i < mod_list_count; i++)
{
mod = mod_list[i];
/* in mode FLUID_VOICE_OVERWRITE disabled instruments modulators CANNOT be skipped. */
/* in mode FLUID_VOICE_ADD disabled preset modulators can be skipped. */
if((mode == FLUID_VOICE_OVERWRITE) || (mod->amount != 0))
{
/* Instrument modulators -supersede- existing (default) modulators.
SF 2.01 page 69, 'bullet' 6 */
/* Preset modulators -add- to existing instrument modulators.
SF2.01 page 70 first bullet on page */
fluid_voice_add_mod_local(voice, mod, mode, identity_limit_count);
}
}
}
/*
* fluid_defpreset_noteon
@ -695,9 +829,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
fluid_voice_zone_t *voice_zone;
fluid_list_t *list;
fluid_voice_t *voice;
fluid_mod_t *mod;
fluid_mod_t *mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
int mod_list_count;
int i;
global_preset_zone = fluid_defpreset_get_global_zone(defpreset);
@ -769,63 +900,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
} /* for all generators */
/* global instrument zone, modulators: Put them all into a
* list. */
mod_list_count = 0;
if(global_inst_zone)
{
mod = global_inst_zone->mod;
while(mod)
{
mod_list[mod_list_count++] = mod;
mod = mod->next;
}
}
/* local instrument zone, modulators.
* Replace modulators with the same definition in the list:
* SF 2.01 page 69, 'bullet' 8
*/
mod = inst_zone->mod;
while(mod)
{
/* 'Identical' modulators will be deleted by setting their
* list entry to NULL. The list length is known, NULL
* entries will be ignored later. SF2.01 section 9.5.1
* page 69, 'bullet' 3 defines 'identical'. */
for(i = 0; i < mod_list_count; i++)
{
if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i]))
{
mod_list[i] = NULL;
}
}
/* Finally add the new modulator to to the list. */
mod_list[mod_list_count++] = mod;
mod = mod->next;
}
/* Add instrument modulators (global / local) to the voice. */
for(i = 0; i < mod_list_count; i++)
{
mod = mod_list[i];
if(mod != NULL) /* disabled modulators CANNOT be skipped. */
{
/* Instrument modulators -supersede- existing (default)
* modulators. SF 2.01 page 69, 'bullet' 6 */
fluid_voice_add_mod(voice, mod, FLUID_VOICE_OVERWRITE);
}
}
/* Adds instrument zone modulators (global and local) to the voice.*/
fluid_defpreset_noteon_add_mod_to_voice(voice,
/* global instrument modulators */
global_inst_zone ? global_inst_zone->mod : NULL,
inst_zone->mod, /* local instrument modulators */
FLUID_VOICE_OVERWRITE); /* mode */
/* Preset level, generators */
@ -868,57 +948,12 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
}
} /* for all generators */
/* Global preset zone, modulators: put them all into a
* list. */
mod_list_count = 0;
if(global_preset_zone)
{
mod = global_preset_zone->mod;
while(mod)
{
mod_list[mod_list_count++] = mod;
mod = mod->next;
}
}
/* Process the modulators of the local preset zone. Kick
* out all identical modulators from the global preset zone
* (SF 2.01 page 69, second-last bullet) */
mod = preset_zone->mod;
while(mod)
{
for(i = 0; i < mod_list_count; i++)
{
if(mod_list[i] && fluid_mod_test_identity(mod, mod_list[i]))
{
mod_list[i] = NULL;
}
}
/* Finally add the new modulator to the list. */
mod_list[mod_list_count++] = mod;
mod = mod->next;
}
/* Add preset modulators (global / local) to the voice. */
for(i = 0; i < mod_list_count; i++)
{
mod = mod_list[i];
if((mod != NULL) && (mod->amount != 0)) /* disabled modulators can be skipped. */
{
/* Preset modulators -add- to existing instrument /
* default modulators. SF2.01 page 70 first bullet on
* page */
fluid_voice_add_mod(voice, mod, FLUID_VOICE_ADD);
}
}
/* Adds preset zone modulators (global and local) to the voice.*/
fluid_defpreset_noteon_add_mod_to_voice(voice,
/* global preset modulators */
global_preset_zone ? global_preset_zone->mod : NULL,
preset_zone->mod, /* local preset modulators */
FLUID_VOICE_ADD); /* mode */
/* add the synthesis process to the synthesis loop. */
fluid_synth_start_voice(synth, voice);
@ -930,7 +965,6 @@ fluid_defpreset_noteon(fluid_defpreset_t *defpreset, fluid_synth_t *synth, int c
* class - for example when using stereo samples)
*/
}
}
}
@ -1110,6 +1144,7 @@ new_fluid_preset_zone(char *name)
static void delete_fluid_list_mod(fluid_mod_t *mod)
{
fluid_mod_t *tmp;
while(mod) /* delete the modulators */
{
tmp = mod;
@ -1192,6 +1227,129 @@ static int fluid_preset_zone_create_voice_zones(fluid_preset_zone_t *preset_zone
return FLUID_OK;
}
/**
* Checks if modulator mod is identic to another modulator in the list
* (specs SF 2.0X 7.4, 7.8).
* @param mod, modulator list.
* @param name, if not NULL, pointer on a string displayed as warning.
* @return TRUE if mod is identic to another modulator, FALSE otherwise.
*/
static int
fluid_zone_is_mod_identic(fluid_mod_t *mod, char *name)
{
fluid_mod_t *next = mod->next;
while(next)
{
/* is mod identic to next ? */
if(fluid_mod_test_identity(mod, next))
{
if(name)
{
FLUID_LOG(FLUID_WARN, "Ignoring identic modulator %s", name);
}
return TRUE;
}
next = next->next;
}
return FALSE;
}
/**
* Limits the number of modulators in a modulator list.
* This is appropriate to internal synthesizer modulators tables
* which have a fixed size (FLUID_NUM_MOD).
*
* @param zone_name, zone name
* @param list_mod, address of pointer on modulator list.
*/
static void fluid_limit_mod_list(char *zone_name, fluid_mod_t **list_mod)
{
int mod_idx = 0; /* modulator index */
fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */
fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */
while(mod)
{
if((mod_idx + 1) > FLUID_NUM_MOD)
{
/* truncation of list_mod */
if(mod_idx)
{
prev_mod->next = NULL;
}
else
{
*list_mod = NULL;
}
delete_fluid_list_mod(mod);
FLUID_LOG(FLUID_WARN, "%s, modulators count limited to %d", zone_name,
FLUID_NUM_MOD);
break;
}
mod_idx++;
prev_mod = mod;
mod = mod->next;
}
}
/**
* Checks and remove invalid modulators from a zone modulators list.
* - checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1).
* - checks identic modulators in the list (specs SF 2.01 7.4, 7.8).
* @param zone_name, zone name.
* @param list_mod, address of pointer on modulators list.
*/
static void
fluid_zone_check_mod(char *zone_name, fluid_mod_t **list_mod)
{
fluid_mod_t *prev_mod = NULL; /* previous modulator in list_mod */
fluid_mod_t *mod = *list_mod; /* first modulator in list_mod */
int mod_idx = 0; /* modulator index */
while(mod)
{
char zone_mod_name[256];
fluid_mod_t *next = mod->next;
/* prepare modulator name: zonename/#modulator */
FLUID_SNPRINTF(zone_mod_name, sizeof(zone_mod_name), "%s/mod%d", zone_name, mod_idx);
/* has mod invalid sources ? */
if(!fluid_mod_check_sources(mod, zone_mod_name)
/* or is mod identic to any following modulator ? */
|| fluid_zone_is_mod_identic(mod, zone_mod_name))
{
/* the modulator is useless so we remove it */
if(prev_mod)
{
prev_mod->next = next;
}
else
{
*list_mod = next;
}
delete_fluid_mod(mod); /* freeing */
}
else
{
prev_mod = mod;
}
mod = next;
mod_idx++;
}
/* limits the size of modulators list */
fluid_limit_mod_list(zone_name, list_mod);
}
/*
* fluid_zone_gen_import_sfont
* Imports generators from sfzone to gen and range.
@ -1205,7 +1363,7 @@ fluid_zone_gen_import_sfont(fluid_gen_t *gen, fluid_zone_range_t *range, SFZone
fluid_list_t *r;
SFGen *sfgen;
for( r = sfzone->gen; r != NULL; )
for(r = sfzone->gen; r != NULL;)
{
sfgen = (SFGen *)fluid_list_get(r);
@ -1258,6 +1416,7 @@ fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, uns
/* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
flags_dest = 0;
if(sf_source & (1 << 7))
{
flags_dest |= FLUID_MOD_CC;
@ -1313,19 +1472,21 @@ fluid_zone_mod_source_import_sfont(unsigned char *src, unsigned char *flags, uns
/* This shouldn't happen - unknown type! */
return FALSE;
}
*flags = flags_dest;
return TRUE;
}
/*
* fluid_zone_mod_import_sfont
* Imports modulators from sfzone to mod list.
* @param mod, pointer on modulator list to return.
* Imports modulators from sfzone to modulators list mod.
* @param zone_name, zone name.
* @param mod, address of pointer on modulators list to return.
* @param sfzone, pointer on soundfont zone.
* @return FLUID_OK if success, FLUID_FAILED otherwise.
*/
static int
fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone)
fluid_zone_mod_import_sfont(char *zone_name, fluid_mod_t **mod, SFZone *sfzone)
{
fluid_list_t *r;
int count;
@ -1367,7 +1528,7 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone)
mod_dest->amount = 0;
}
/* *** Dest *** */
/* *** Dest *** */
mod_dest->dest = mod_src->dest; /* index of controlled generator */
/* *** Amount source *** */
@ -1422,6 +1583,8 @@ fluid_zone_mod_import_sfont(fluid_mod_t **mod, SFZone *sfzone)
r = fluid_list_next(r);
} /* foreach modulator */
/* checks and removes invalid modulators in modulators list*/
fluid_zone_check_mod(zone_name, mod);
return FLUID_OK;
}
@ -1457,7 +1620,7 @@ fluid_preset_zone_import_sfont(fluid_preset_zone_t *zone, SFZone *sfzone, fluid_
}
/* Import the modulators (only SF2.1 and higher) */
return fluid_zone_mod_import_sfont(&zone->mod, sfzone);
return fluid_zone_mod_import_sfont(zone->name, &zone->mod, sfzone);
}
/*
@ -1570,7 +1733,9 @@ fluid_inst_import_sfont(fluid_preset_zone_t *preset_zone, SFInst *sfinst, fluid_
{
sfzone = (SFZone *)fluid_list_get(p);
FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%d", inst->name, count);
/* integrates preset zone name in instrument zone name */
FLUID_SNPRINTF(zone_name, sizeof(zone_name), "%s/%s/%d", preset_zone->name,
inst->name, count);
inst_zone = new_fluid_inst_zone(zone_name);
@ -1728,7 +1893,7 @@ fluid_inst_zone_import_sfont(fluid_inst_zone_t *inst_zone, SFZone *sfzone, fluid
}
/* Import the modulators (only SF2.1 and higher) */
return fluid_zone_mod_import_sfont(&inst_zone->mod, sfzone);
return fluid_zone_mod_import_sfont(inst_zone->name, &inst_zone->mod, sfzone);
}
/*

View File

@ -504,6 +504,177 @@ size_t fluid_mod_sizeof()
return sizeof(fluid_mod_t);
}
/**
* Checks if modulator with source 1 other than CC is FLUID_MOD_NONE.
*
* @param mod, modulator.
* @return TRUE if modulator source 1 other than cc is FLUID_MOD_NONE, FALSE otherwise.
*/
static int
fluid_mod_is_src1_none(const fluid_mod_t *mod)
{
return(((mod->flags1 & FLUID_MOD_CC) == 0) && (mod->src1 == FLUID_MOD_NONE));
}
/**
* Checks if modulators source other than CC source is invalid.
* (specs SF 2.01 7.4, 7.8, 8.2.1)
*
* @param mod, modulator.
* @param src1_select, source input selection to check.
* 1 to check src1 source.
* 0 to check src2 source.
* @return FALSE if selected modulator source other than cc is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_non_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) != 0) /* src is a CC */
/* SF2.01 section 8.2.1: Constant value */
|| ((src == FLUID_MOD_NONE)
|| (src == FLUID_MOD_VELOCITY) /* Note-on velocity */
|| (src == FLUID_MOD_KEY) /* Note-on key number */
|| (src == FLUID_MOD_KEYPRESSURE) /* Poly pressure */
|| (src == FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
|| (src == FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
|| (src == FLUID_MOD_PITCHWHEELSENS) /* Pitch wheel sensitivity */
));
}
/**
* Checks if modulator CC source is invalid (specs SF 2.01 7.4, 7.8, 8.2.1).
* @param mod, modulator.
* @src1_select, source input selection:
* 1 to check src1 source or
* 0 to check src2 source.
* @return FALSE if selected modulator's source CC is invalid, TRUE otherwise.
*/
static int
fluid_mod_check_cc_source(const fluid_mod_t *mod, unsigned char src1_select)
{
unsigned char flags, src;
if(src1_select)
{
flags = mod->flags1;
src = mod->src1;
}
else
{
flags = mod->flags2;
src = mod->src2;
}
return(((flags & FLUID_MOD_CC) == 0) /* src is non CC */
|| ((src != BANK_SELECT_MSB)
&& (src != BANK_SELECT_LSB)
&& (src != DATA_ENTRY_MSB)
&& (src != DATA_ENTRY_LSB)
/* is src not NRPN_LSB, NRPN_MSB, RPN_LSB, RPN_MSB */
&& ((src < NRPN_LSB) || (RPN_MSB < src))
/* is src not ALL_SOUND_OFF, ALL_CTRL_OFF, LOCAL_CONTROL, ALL_NOTES_OFF ? */
/* is src not OMNI_OFF, OMNI_ON, POLY_OFF, POLY_ON ? */
&& (src < ALL_SOUND_OFF)
/* CC lsb shouldn't allowed to modulate (spec SF 2.01 - 8.2.1)
However, as long fluidsynth will use only CC 7 bits resolution,
it is safe to ignore these SF recommendations on CC receive.
See explanations in fluid_synth_cc_LOCAL() */
/* uncomment next line to forbid CC lsb */
/* && ((src < 32) || (63 < src)) */
));
}
/**
* Checks valid modulator sources (specs SF 2.01 7.4, 7.8, 8.2.1)
* @param mod, modulator.
* @param name,if not NULL, pointer on a string displayed as a warning.
* @return TRUE if modulator sources src1, src2 are valid, FALSE otherwise.
*/
int fluid_mod_check_sources(const fluid_mod_t *mod, char *name)
{
static const char *invalid_non_cc_src =
"Invalid modulator, using non-CC source %s.src%d=%d";
static const char *invalid_cc_src =
"Invalid modulator, using CC source %s.src%d=%d";
static const char *src1_is_none =
"Modulator with source 1 none %s.src1=%d";
/* checks valid non cc sources */
if(!fluid_mod_check_non_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 1, mod->src1);
}
return FALSE;
}
/*
When src1 is non CC source FLUID_MOD_NONE, the modulator is valid but
the output of this modulator will be forced to 0 at synthesis time.
Also this modulator cannot be used to overwrite a default modulator (as
there is no default modulator with src1 source equal to FLUID_MOD_NONE).
Consequently it is useful to return FALSE to indicate this modulator
being useless. It will be removed later with others invalid modulators.
*/
if(fluid_mod_is_src1_none(mod))
{
if(name)
{
FLUID_LOG(FLUID_WARN, src1_is_none, name, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_non_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_non_cc_src, name, 2, mod->src2);
}
return FALSE;
}
/* checks valid cc sources */
if(!fluid_mod_check_cc_source(mod, 1)) /* check src1 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 1, mod->src1);
}
return FALSE;
}
if(!fluid_mod_check_cc_source(mod, 0)) /* check src2 */
{
if(name)
{
FLUID_LOG(FLUID_WARN, invalid_cc_src, name, 2, mod->src2);
}
return FALSE;
}
return TRUE;
}
/**
* Checks if two modulators are identical in sources, flags and destination.
* @param mod1 First modulator

View File

@ -44,6 +44,7 @@ struct _fluid_mod_t
};
fluid_real_t fluid_mod_get_value(fluid_mod_t *mod, fluid_voice_t *voice);
int fluid_mod_check_sources(const fluid_mod_t *mod, char *name);
#ifdef DEBUG
void fluid_dump_modulator(fluid_mod_t *mod);

View File

@ -227,7 +227,7 @@ void fluid_synth_settings(fluid_settings_t *settings)
#else
fluid_settings_register_int(settings, "synth.cpu-cores", 1, 1, 1, 0);
#endif
fluid_settings_register_int(settings, "synth.min-note-length", 10, 0, 65535, 0);
fluid_settings_register_int(settings, "synth.threadsafe-api", 1, 0, 1, FLUID_HINT_TOGGLED);
@ -1331,6 +1331,13 @@ fluid_synth_add_default_mod(fluid_synth_t *synth, const fluid_mod_t *mod, int mo
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(mod != NULL, FLUID_FAILED);
/* Checks if modulators sources are valid */
if(!fluid_mod_check_sources(mod, "api fluid_synth_add_default_mod mod"))
{
return FLUID_FAILED;
}
fluid_synth_api_enter(synth);
default_mod = synth->default_mod;
@ -3369,7 +3376,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(left != NULL, FLUID_FAILED);
fluid_return_val_if_fail(right != NULL, FLUID_FAILED);
/* First, take what's still available in the buffer */
count = 0;
num = synth->cur;
@ -3522,7 +3529,7 @@ fluid_synth_nwrite_float(fluid_synth_t *synth, int len,
/**
* mixes the samples of \p in to \p out
*
*
* @param out the output sample buffer to mix to
* @param ooff sample offset in \p out
* @param in the rvoice_mixer input sample buffer to mix from
@ -3540,6 +3547,7 @@ static FLUID_INLINE void fluid_synth_mix_single_buffer(float *FLUID_RESTRICT out
if(out != NULL)
{
int j;
for(j = 0; j < num; j++)
{
out[j + ooff] += (float) in[buf_idx * FLUID_BUFSIZE * FLUID_MIXER_MAX_BUFFERS_DEFAULT + j + ioff];
@ -3688,7 +3696,7 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, 0, fx_left_in, synth->cur, buf_idx, num);
@ -3731,10 +3739,10 @@ fluid_synth_process(fluid_synth_t *synth, int len, int nfx, float *fx[],
for(i = 0; i < nfxchan; i++)
{
int buf_idx = f * nfxchan + i;
float *out_buf = fx[(buf_idx * 2) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_left_in, 0, buf_idx, num);
out_buf = fx[(buf_idx * 2 + 1) % nfx];
fluid_synth_mix_single_buffer(out_buf, count, fx_right_in, 0, buf_idx, num);
}
@ -3785,7 +3793,7 @@ fluid_synth_write_float(fluid_synth_t *synth, int len,
float cpu_load;
fluid_profile_ref_var(prof_ref);
fluid_return_val_if_fail(synth != NULL, FLUID_FAILED);
fluid_return_val_if_fail(lout != NULL, FLUID_FAILED);
fluid_return_val_if_fail(rout != NULL, FLUID_FAILED);
@ -3854,10 +3862,12 @@ static FLUID_INLINE int16_t
round_clip_to_i16(float x)
{
long i;
if(x >= 0.0f)
{
i = (long)(x + 0.5f);
if (FLUID_UNLIKELY(i > 32767))
if(FLUID_UNLIKELY(i > 32767))
{
i = 32767;
}
@ -3865,12 +3875,13 @@ round_clip_to_i16(float x)
else
{
i = (long)(x - 0.5f);
if (FLUID_UNLIKELY(i < -32768))
if(FLUID_UNLIKELY(i < -32768))
{
i = -32768;
}
}
return (int16_t)i;
}
@ -4348,11 +4359,11 @@ fluid_synth_alloc_voice_LOCAL(fluid_synth_t *synth, fluid_sample_t *sample, int
)
{
// Replacement of default_vel2att modulator by custom_breath2att_modulator
fluid_voice_add_mod(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT);
fluid_voice_add_mod_local(voice, &custom_breath2att_mod, FLUID_VOICE_DEFAULT, 0);
}
else
{
fluid_voice_add_mod(voice, default_mod, FLUID_VOICE_DEFAULT);
fluid_voice_add_mod_local(voice, default_mod, FLUID_VOICE_DEFAULT, 0);
}
// Next default modulator to add to the voice

View File

@ -1185,7 +1185,7 @@ fluid_voice_update_param(fluid_voice_t *voice, int gen)
*/
#define NBR_BIT_BY_VAR_LN2 5 /* for 32 bits variables */
#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2)
#define NBR_BIT_BY_VAR (1 << NBR_BIT_BY_VAR_LN2)
#define NBR_BIT_BY_VAR_ANDMASK (NBR_BIT_BY_VAR - 1)
#define SIZE_UPDATED_GEN_BIT ((GEN_LAST + NBR_BIT_BY_VAR_ANDMASK) / NBR_BIT_BY_VAR)
@ -1209,7 +1209,7 @@ int fluid_voice_modulate(fluid_voice_t *voice, int cc, int ctrl)
mod = &voice->mod[i];
/* step 1: find all the modulators that have the changed controller
as input source. When ctrl is -1 all modulators destination
as input source. When ctrl is -1 all modulators destination
are updated */
if(ctrl < 0 || fluid_mod_has_source(mod, cc, ctrl))
{
@ -1465,10 +1465,10 @@ fluid_voice_stop(fluid_voice_t *voice)
}
/**
* Adds a modulator to the voice.
* @param voice Voice instance
* @param mod Modulator info (copied)
* @param mode Determines how to handle an existing identical modulator
* Adds a modulator to the voice if the modulator has valid sources.
* @param voice Voice instance.
* @param mod Modulator info (copied).
* @param mode Determines how to handle an existing identical modulator.
* #FLUID_VOICE_ADD to add (offset) the modulator amounts,
* #FLUID_VOICE_OVERWRITE to replace the modulator,
* #FLUID_VOICE_DEFAULT when adding a default modulator - no duplicate should
@ -1476,33 +1476,43 @@ fluid_voice_stop(fluid_voice_t *voice)
*/
void
fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* Ignore the modulator if its sources inputs are invalid */
if(fluid_mod_check_sources(mod, "api fluid_voice_add_mod mod"))
{
fluid_voice_add_mod_local(voice, mod, mode, FLUID_NUM_MOD);
}
}
/**
* Adds a modulator to the voice.
* local version of fluid_voice_add_mod function. Called at noteon time.
* @param voice, mod, mode, same as for fluid_voice_add_mod() (see above).
* @param check_limit_count is the modulator number limit to handle with existing
* identical modulator(i.e mode FLUID_VOICE_OVERWRITE, FLUID_VOICE_ADD).
* - When FLUID_NUM_MOD, all the voices modulators (since the previous call)
* are checked for identity.
* - When check_count_limit is below the actual number of voices modulators
* (voice->mod_count), this will restrict identity check to this number,
* This is usefull when we know by advance that there is no duplicate with
* modulators at index above this limit. This avoid wasting cpu cycles at noteon.
*/
void
fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count)
{
int i;
/*
* Some soundfonts come with a huge number of non-standard
* controllers, because they have been designed for one particular
* sound card. Discard them, maybe print a warning.
*/
if(((mod->flags1 & FLUID_MOD_CC) == 0)
&& ((mod->src1 != FLUID_MOD_NONE) /* SF2.01 section 8.2.1: Constant value */
&& (mod->src1 != FLUID_MOD_VELOCITY) /* Note-on velocity */
&& (mod->src1 != FLUID_MOD_KEY) /* Note-on key number */
&& (mod->src1 != FLUID_MOD_KEYPRESSURE) /* Poly pressure */
&& (mod->src1 != FLUID_MOD_CHANNELPRESSURE) /* Channel pressure */
&& (mod->src1 != FLUID_MOD_PITCHWHEEL) /* Pitch wheel */
&& (mod->src1 != FLUID_MOD_PITCHWHEELSENS)))/* Pitch wheel sensitivity */
/* check_limit_count cannot be above voice->mod_count */
if(check_limit_count > voice->mod_count)
{
FLUID_LOG(FLUID_WARN, "Ignoring invalid controller, using non-CC source %i.", mod->src1);
return;
check_limit_count = voice->mod_count;
}
if(mode == FLUID_VOICE_ADD)
{
/* if identical modulator exists, add them */
for(i = 0; i < voice->mod_count; i++)
for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{
@ -1517,7 +1527,7 @@ fluid_voice_add_mod(fluid_voice_t *voice, fluid_mod_t *mod, int mode)
{
/* if identical modulator exists, replace it (only the amount has to be changed) */
for(i = 0; i < voice->mod_count; i++)
for(i = 0; i < check_limit_count; i++)
{
if(fluid_mod_test_identity(&voice->mod[i], mod))
{

View File

@ -157,6 +157,7 @@ void fluid_voice_release(fluid_voice_t *voice);
void fluid_voice_noteoff(fluid_voice_t *voice);
void fluid_voice_off(fluid_voice_t *voice);
void fluid_voice_stop(fluid_voice_t *voice);
void fluid_voice_add_mod_local(fluid_voice_t *voice, fluid_mod_t *mod, int mode, int check_limit_count);
void fluid_voice_overflow_rvoice_finished(fluid_voice_t *voice);
int fluid_voice_kill_excl(fluid_voice_t *voice);