Support loading SoundFonts >2GiB on Windows (#629)

Since sizeof(long) == 4 even on 64 bit Windose, big files cannot be
loaded natively via the ANSI C file API. This change makes fluidsynth's
file callback API use long long, which is guaranteed to be at least 64
bit wide.
This commit is contained in:
Tom M 2020-05-26 16:53:59 +02:00 committed by GitHub
parent 19a20eb852
commit 9995fd88b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 84 additions and 16 deletions

View file

@ -146,12 +146,14 @@ jobs:
gtk-bundle: $(gtk-bundle-x86)
libsndfile-url: $(libsndfile-url-x86)
mingw-url: $(mingw-url-x86)
artifact-prefix: "fluidsynth-mingw"
x64:
CMAKE_FLAGS:
platform: x64
gtk-bundle: $(gtk-bundle-x64)
libsndfile-url: $(libsndfile-url-x64)
mingw-url: $(mingw-url-x64)
artifact-prefix: "fluidsynth-mingw"
pool:
vmImage: 'vs2017-win2016'
steps:
@ -189,7 +191,7 @@ jobs:
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
pkg-config --list-all
mkdir build && cd build || exit -1
cmake -Werror=dev -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) $(CMAKE_FLAGS) -Denable-readline=0 -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1
cmake -Werror=dev -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX=$(Build.ArtifactStagingDirectory) $(CMAKE_FLAGS) -Denable-readline=0 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=1 -DNO_GUI=1 .. || exit -1
mingw32-make.exe all || exit -1
displayName: 'Compile fluidsynth'
- script: |
@ -199,5 +201,21 @@ jobs:
set PATH=%PATH:C:\Program Files\Git\bin;=%
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
cd build || exit -1
mingw32-make.exe check || exit -1
mingw32-make.exe check || cd .
displayName: 'Execute Unittests'
- script: |
@ECHO ON
cd build
mingw32-make.exe install || exit -1
xcopy test $(Build.ArtifactStagingDirectory)\bin /s
del $(Build.ArtifactStagingDirectory)\bin\concrt*.dll
del $(Build.ArtifactStagingDirectory)\bin\vcruntime*.dll
del $(Build.ArtifactStagingDirectory)\bin\msvcp*.dll
del $(Build.ArtifactStagingDirectory)\lib\instpatch*.lib
del $(Build.ArtifactStagingDirectory)\lib\pkgconfig\libinstpatch*.pc
rd $(Build.ArtifactStagingDirectory)\include\libinstpatch-2 /s /q
displayName: 'Copy Artifacts'
- task: PublishBuildArtifacts@1
inputs:
pathtoPublish: $(Build.ArtifactStagingDirectory)
artifactName: $(artifact-prefix)-$(platform)

View file

@ -42,9 +42,9 @@ set ( FLUIDSYNTH_VERSION "\"${VERSION}\"" )
# if any interfaces have been added: AGE++
# if any interfaces have been removed/changed (compatibility broken): AGE=0
# This is not exactly the same algorithm as the libtool one, but the results are the same.
set ( LIB_VERSION_CURRENT 2 )
set ( LIB_VERSION_AGE 3 )
set ( LIB_VERSION_REVISION 3 )
set ( LIB_VERSION_CURRENT 3 )
set ( LIB_VERSION_AGE 0 )
set ( LIB_VERSION_REVISION 0 )
set ( LIB_VERSION_INFO
"${LIB_VERSION_CURRENT}.${LIB_VERSION_AGE}.${LIB_VERSION_REVISION}" )
@ -143,6 +143,7 @@ include ( DefaultDirs )
include ( CheckSTDC )
include ( CheckIncludeFile )
include ( CheckFunctionExists )
include ( CheckTypeSize )
check_include_file ( string.h HAVE_STRING_H )
check_include_file ( stdlib.h HAVE_STDLIB_H )
check_include_file ( stdio.h HAVE_STDIO_H )
@ -164,6 +165,11 @@ check_include_file ( pthread.h HAVE_PTHREAD_H )
check_include_file ( signal.h HAVE_SIGNAL_H )
check_include_file ( getopt.h HAVE_GETOPT_H )
check_include_file ( stdint.h HAVE_STDINT_H )
check_type_size ( "long long" LONG_LONG )
if ( NOT HAVE_LONG_LONG AND NOT MSVC)
message ( FATAL_ERROR "Your compiler does not support intrinsic type 'long long'. Unable to compile fluidsynth." )
endif ()
include ( TestInline )
include ( TestVLA )
include ( TestBigEndian )

View file

@ -69,6 +69,10 @@ What is FluidSynth?
- FluidSynth is open source, in active development. For more details, take a look at http://www.fluidsynth.org
\section NewIn2_2_0 What's new in 2.2.0?
- #fluid_file_callbacks_t now uses <code>long long</code> as file-offset type (see #fluid_long_long_t). This is a breaking change, which allows to load SoundFonts bigger than 2GiB on Windows. This change required to bump fluidsynth's SOVERSION.
\section NewIn2_1_1 What's new in 2.1.1?
- requirements for explicit sequencer client unregistering have been relaxed: delete_fluid_sequencer() now correctly frees any registered sequencer clients (clients can still be explicitly unregistered)

View file

@ -124,7 +124,7 @@ typedef void *(* fluid_sfloader_callback_open_t)(const char *filename);
*
* @return returns #FLUID_OK if exactly \c count bytes were successfully read, else returns #FLUID_FAILED and leaves \a buf unmodified.
*/
typedef int (* fluid_sfloader_callback_read_t)(void *buf, int count, void *handle);
typedef int (* fluid_sfloader_callback_read_t)(void *buf, fluid_long_long_t count, void *handle);
/**
* Same purpose and behaviour as fseek.
@ -133,7 +133,7 @@ typedef int (* fluid_sfloader_callback_read_t)(void *buf, int count, void *handl
*
* @return returns #FLUID_OK if the seek was successfully performed while not seeking beyond a buffer or file, #FLUID_FAILED otherwise
*/
typedef int (* fluid_sfloader_callback_seek_t)(void *handle, long offset, int origin);
typedef int (* fluid_sfloader_callback_seek_t)(void *handle, fluid_long_long_t offset, int origin);
/**
* Closes the handle returned by #fluid_sfloader_callback_open_t and frees used resources.
@ -143,7 +143,7 @@ typedef int (* fluid_sfloader_callback_seek_t)(void *handle, long offset, int or
typedef int (* fluid_sfloader_callback_close_t)(void *handle);
/** @return returns current file offset or #FLUID_FAILED on error */
typedef long (* fluid_sfloader_callback_tell_t)(void *handle);
typedef fluid_long_long_t (* fluid_sfloader_callback_tell_t)(void *handle);
FLUIDSYNTH_API int fluid_sfloader_set_callbacks(fluid_sfloader_t *loader,

View file

@ -64,6 +64,16 @@ typedef int fluid_ostream_t; /**< Output stream descriptor */
typedef short fluid_seq_id_t; /**< Unique client IDs used by the sequencer and #fluid_event_t, obtained by fluid_sequencer_register_client() and fluid_sequencer_register_fluidsynth() */
#if defined(_MSC_VER) && (_MSC_VER < 1800)
typedef __int64 fluid_long_long_t; // even on 32bit windows
#else
/**
* A typedef for C99's type long long, which is at least 64-bit wide, as guaranteed by the C99.
* @p __int64 will be used as replacement for VisualStudio 2010 and older.
*/
typedef long long fluid_long_long_t;
#endif
#ifdef __cplusplus
}
#endif

View file

@ -405,7 +405,7 @@ int fluid_is_soundfont(const char *filename)
SFData *fluid_sffile_open(const char *fname, const fluid_file_callbacks_t *fcbs)
{
SFData *sf;
int fsize = 0;
fluid_long_long_t fsize = 0;
if(!(sf = FLUID_NEW(SFData)))
{

View file

@ -40,18 +40,18 @@ int default_fclose(void *handle)
return FLUID_FCLOSE((FILE *)handle) == 0 ? FLUID_OK : FLUID_FAILED;
}
long default_ftell(void *handle)
fluid_long_long_t default_ftell(void *handle)
{
return FLUID_FTELL((FILE *)handle);
}
int safe_fread(void *buf, int count, void *fd)
int safe_fread(void *buf, fluid_long_long_t count, void *fd)
{
if(FLUID_FREAD(buf, count, 1, (FILE *)fd) != 1)
if(FLUID_FREAD(buf, (size_t)count, 1, (FILE *)fd) != 1)
{
if(feof((FILE *)fd))
{
FLUID_LOG(FLUID_ERR, "EOF while attempting to read %d bytes", count);
FLUID_LOG(FLUID_ERR, "EOF while attempting to read %lld bytes", count);
}
else
{
@ -64,11 +64,11 @@ int safe_fread(void *buf, int count, void *fd)
return FLUID_OK;
}
int safe_fseek(void *fd, long ofs, int whence)
int safe_fseek(void *fd, fluid_long_long_t ofs, int whence)
{
if(FLUID_FSEEK((FILE *)fd, ofs, whence) != 0)
{
FLUID_LOG(FLUID_ERR, "File seek failed with offset = %ld and whence = %d", ofs, whence);
FLUID_LOG(FLUID_ERR, "File seek failed with offset = %lld and whence = %d", ofs, whence);
return FLUID_FAILED;
}

View file

@ -1670,3 +1670,25 @@ FILE* fluid_file_open(const char* path, const char** errMsg)
return handle;
}
fluid_long_long_t fluid_file_tell(FILE* f)
{
#ifdef WIN32
// On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB.
// We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be
// availble on WindowsXP, Win98, etc.
//
// The web recommends to fallback to _telli64() in this case. However, it's return value differs from
// _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-602238436
//
// Thus, we use fgetpos().
fpos_t pos;
if(fgetpos(f, &pos) != 0)
{
return (fluid_long_long_t)-1L;
}
return pos;
#else
return ftell(f);
#endif
}

View file

@ -499,6 +499,8 @@ typedef GStatBuf fluid_stat_buf_t;
#endif
FILE* fluid_file_open(const char* filename, const char** errMsg);
fluid_long_long_t fluid_file_tell(FILE* f);
/* Profiling */
#if WITH_PROFILING

View file

@ -194,8 +194,14 @@ void* fluid_alloc(size_t len);
#define FLUID_FOPEN(_f,_m) fopen(_f,_m)
#define FLUID_FCLOSE(_f) fclose(_f)
#define FLUID_FREAD(_p,_s,_n,_f) fread(_p,_s,_n,_f)
#ifdef WIN32
#define FLUID_FSEEK(_f,_n,_set) _fseeki64(_f,_n,_set)
#else
#define FLUID_FSEEK(_f,_n,_set) fseek(_f,_n,_set)
#define FLUID_FTELL(_f) ftell(_f)
#endif
#define FLUID_FTELL(_f) fluid_file_tell(_f)
/* Memory functions */
#define FLUID_MEMCPY(_dst,_src,_n) memcpy(_dst,_src,_n)