Use native Win32 API instead of glib on Windows

- Don't look for PkgConfig on Win32.
- Don't force glib and gthread on Win32.
- Support finding libsndfile with vcpkg.
- Add enable-static-msvcrt option to use the static VC runtime library.
- Add stubs that define glib functions to Win32 equivalents.
This commit is contained in:
Marisa Heit 2022-07-24 00:18:12 -05:00
parent 85fcbde9e0
commit 32f4347d26
9 changed files with 466 additions and 0 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
build/
.vs/
CMakeCache.txt
CMakeFiles

View file

@ -68,6 +68,9 @@ option ( enable-portaudio "compile PortAudio support" off )
option ( enable-profiling "profile the dsp code" off )
option ( enable-trap-on-fpe "enable SIGFPE trap on Floating Point Exceptions" off )
option ( enable-ubsan "compile and link against UBSan (for debugging fluidsynth internals)" off )
if ( MSVC )
option ( enable-static-mscrt "use static MS Visual C++ runtime" off )
endif ( MSVC )
# Options enabled by default
option ( enable-aufile "compile support for sound file output" on )
@ -319,6 +322,10 @@ if ( WIN32 )
set ( CMAKE_DEBUG_POSTFIX "_debug" )
endif ( NOT MSVC )
if ( enable-static-msvcrt )
set ( CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" )
endif ( enable-static-msvcrt )
# MinGW compiler (a Windows GCC port)
if ( MINGW )
set ( MINGW32 1 )
@ -475,6 +482,7 @@ if ( ASTYLE )
)
endif(ASTYLE)
if ( NOT WIN32 )
find_package ( PkgConfig REQUIRED )
# Mandatory libraries: glib and gthread
@ -484,15 +492,42 @@ list( APPEND PC_REQUIRES_PRIV "glib-2.0" "gthread-2.0")
if ( GLIB_glib-2.0_VERSION AND GLIB_glib-2.0_VERSION VERSION_LESS "2.26.0" )
message ( WARNING "Your version of glib is very old. This may cause problems with fluidsynth's sample cache on Windows. Consider updating to glib 2.26 or newer!" )
endif ( GLIB_glib-2.0_VERSION AND GLIB_glib-2.0_VERSION VERSION_LESS "2.26.0" )
set ( WIN32_GLIBSTUBS OFF )
set ( WITH_GLIB_STUBS 0 )
else ( NOT WIN32 )
set ( WIN32_GLIBSTUBS ON )
set ( WITH_GLIB_STUBS 1 )
endif ( NOT WIN32 )
include ( PkgConfigHelpers ) # has unset_pkg_config()
if ( NOT PkgConfig_FOUND )
set ( enable-pulseaudio OFF )
set ( enable-alsa OFF )
set ( enable-portaudio OFF )
set ( enable-jack OFF )
set ( enable-pipewire OFF )
set ( enable-lash OFF )
set ( enable-systemd OFF )
set ( enable-dbus OFF )
set ( enable-ladspa OFF )
set ( enable-libinstpatch OFF )
set ( enable-sdl2 OFF )
set ( enable-oboe OFF )
set ( enable-readline OFF )
endif ( NOT PkgConfig_FOUND )
# Optional features
unset ( LIBSNDFILE_SUPPORT CACHE )
unset ( LIBSNDFILE_HASVORBIS CACHE )
if ( enable-libsndfile )
if ( PkgConfig_FOUND )
pkg_check_modules ( LIBSNDFILE sndfile>=1.0.0 IMPORTED_TARGET )
set ( LIBSNDFILE_SUPPORT ${LIBSNDFILE_FOUND} )
else ( PkgConfig_FOUND )
find_package(SndFile CONFIG)
set ( LIBSNDFILE_SUPPORT ${SNDFILE_FOUND} )
endif ( PkgConfig_FOUND)
if ( LIBSNDFILE_SUPPORT )
#[[ cmake_print_variables (
LIBSNDFILE_STATIC_LIBRARIES
@ -501,6 +536,7 @@ if ( LIBSNDFILE_SUPPORT )
LIBSNDFILE_STATIC_LDFLAGS_OTHER ) ]]
list( APPEND PC_REQUIRES_PRIV "sndfile")
if ( LIBSNDFILE_STATIC_LIBRARIES MATCHES "vorbis" OR
SndFile_WITH_EXTERNAL_LIBS OR
LIBSNDFILE_STATIC_LDFLAGS MATCHES "vorbis" OR
LIBSNDFILE_STATIC_LDFLAGS_OTHER MATCHES "vorbis" )
set ( LIBSNDFILE_HASVORBIS 1 )
@ -835,9 +871,11 @@ else ()
set ( includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}" )
endif ()
if ( PkgConfig_FOUND )
generate_pkgconfig_spec(fluidsynth.pc.in ${FluidSynth_BINARY_DIR}/fluidsynth.pc libfluidsynth-OBJ)
install ( FILES ${FluidSynth_BINARY_DIR}/fluidsynth.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig )
endif(PkgConfig_FOUND)
# Exported targets for cmake: find_package(FluidSynth)
# when installed, use CMAKE_PREFIX_PATH=fluidsynth-prefix;...

View file

@ -246,6 +246,14 @@ else ( ENABLE_COVERAGE )
set ( DEVEL_REPORT "${DEVEL_REPORT} Coverage: no\n" )
endif ( ENABLE_COVERAGE )
if ( MSVC )
if ( enable-static-msvcrt )
set ( DEVEL_REPORT "${DEVEL_REPORT} Static MSVC Runtime: yes\n" )
else ( enable-static-msvcrt )
set ( DEVEL_REPORT "${DEVEL_REPORT} Static MSVC Runtime: no\n" )
endif ( enable-static-msvcrt )
endif ( MSVC )
message( STATUS
"\n**************************************************************\n"
"Build Summary:\n"

View file

@ -182,6 +182,14 @@ set ( libfluidsynth_SOURCES
bindings/fluid_ladspa.h
)
if ( WIN32_GLIBSTUBS )
set( libfluidsynth_SOURCES
${libfluidsynth_SOURCES}
utils/win32_glibstubs.c
utils/win32_glibstubs.h
)
endif ( WIN32_GLIBSTUBS )
set ( public_HEADERS
${FluidSynth_SOURCE_DIR}/include/fluidsynth/audio.h
${FluidSynth_SOURCE_DIR}/include/fluidsynth/event.h
@ -358,6 +366,11 @@ if ( TARGET PkgConfig::LIBSNDFILE AND LIBSNDFILE_SUPPORT )
target_link_libraries ( libfluidsynth-OBJ PUBLIC PkgConfig::LIBSNDFILE )
endif()
if ( TARGET SndFile::sndfile AND LIBSNDFILE_SUPPORT )
target_include_directories ( libfluidsynth-OBJ PRIVATE ${SndFile_INCLUDE_DIR} )
target_link_libraries ( libfluidsynth-OBJ PUBLIC SndFile::sndfile )
endif()
if ( TARGET PkgConfig::PULSE AND PULSE_SUPPORT )
target_link_libraries ( libfluidsynth-OBJ PUBLIC PkgConfig::PULSE )
endif()
@ -440,6 +453,10 @@ target_link_libraries ( libfluidsynth PRIVATE libfluidsynth-OBJ )
set ( fluidsynth_SOURCES fluidsynth.c )
if ( WIN32_GLIBSTUBS )
set ( fluidsynth_SOURCES ${fluidsynth_SOURCES} utils/win32_glibstubs.c )
endif ( WIN32_GLIBSTUBS )
if ( WASAPI_SUPPORT )
set ( fluidsynth_SOURCES ${fluidsynth_SOURCES} fluid_wasapi_device_enumerate.c )
endif ( WASAPI_SUPPORT )

View file

@ -268,4 +268,7 @@
/* Define to 1 if you have the socklen_t type. */
#cmakedefine HAVE_SOCKLEN_T @HAVE_SOCKLEN_T@
/* Define if using glib stubs instead of real glib. */
#cmakedefine WITH_GLIB_STUBS
#endif /* CONFIG_H */

View file

@ -132,6 +132,9 @@ typedef gintptr intptr_t;
#if defined(WIN32) && HAVE_WINDOWS_H
#include <winsock2.h>
#include <ws2tcpip.h> /* Provides also socklen_t */
#ifdef WITH_GLIB_STUBS
#include "win32_glibstubs.h"
#endif
/* WIN32 special defines */
#define STDIN_FILENO 0
@ -157,7 +160,9 @@ typedef gintptr intptr_t;
#include <gmodule.h>
#endif
#ifndef WIN32
#include <glib/gstdio.h>
#endif
/**
* Macro used for safely accessing a message from a GError and using a default

View file

@ -31,7 +31,9 @@
#include "config.h"
#ifndef WITH_GLIB_STUBS
#include <glib.h>
#endif
#if HAVE_STDLIB_H
#include <stdlib.h> // malloc, free
@ -73,8 +75,13 @@ typedef double fluid_real_t;
/** Atomic types */
#if defined(WIN32) && defined(WITH_GLIB_STUBS)
typedef long fluid_atomic_int_t;
typedef unsigned long fluid_atomic_uint_t;
#else
typedef int fluid_atomic_int_t;
typedef unsigned int fluid_atomic_uint_t;
#endif
typedef float fluid_atomic_float_t;

250
src/utils/win32_glibstubs.c Normal file
View file

@ -0,0 +1,250 @@
#ifdef WIN32
#define _CRT_SECURE_NO_WARNINGS
#include "fluidsynth_priv.h"
#include "fluid_sys.h"
#include "win32_glibstubs.h"
#include <process.h>
static wchar_t *utf8_to_wc(const char *str)
{
if (str == NULL)
{
return NULL;
}
wchar_t *wstr = NULL;
int length;
if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, NULL, 0)) == 0)
{
return NULL;
}
wstr = malloc(length * sizeof(wchar_t));
if (wstr == NULL)
{
return NULL;
}
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, wstr, length);
return wstr;
}
BOOL fluid_g_file_test(const char *pathA, int flags)
{
wchar_t *path = utf8_to_wc(pathA);
if (path == NULL)
{
return FALSE;
}
DWORD attributes = GetFileAttributesW(path);
FLUID_FREE(path);
if (attributes == INVALID_FILE_ATTRIBUTES)
{
return FALSE;
}
if (flags & G_FILE_TEST_EXISTS)
{
return TRUE;
}
if (flags & G_FILE_TEST_IS_REGULAR)
{
return (attributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0;
}
return FALSE;
}
// ParseCommandLine - Taken from ZDoom
//
// Parse a command line (passed in args). If argc is non-NULL, it will
// be set to the number of arguments. If argv is non-NULL, it will be
// filled with pointers to each argument; argv[0] should be initialized
// to point to a buffer large enough to hold all the arguments. The
// return value is the necessary size of this buffer.
//
// Special processing:
// Inside quoted strings, \" becomes just "
// \\ becomes just a single backslash
static long ParseCommandLine(const char *args, int *argc, char **argv)
{
int count;
char *buffer;
long buffplace;
count = 0;
buffplace = 0;
buffer = argv != NULL ? argv[0] : NULL;
for (;;)
{
while (*args <= ' ' && *args)
{ // skip white space
args++;
}
if (*args == 0)
{
break;
}
else if (*args == '\"')
{ // read quoted string
char stuff;
if (argv != NULL)
{
argv[count] = buffer + buffplace;
}
count++;
args++;
do
{
stuff = *args++;
if (stuff == '\\' && *args == '\"')
{
stuff = '\"';
args++;
}
else if (stuff == '\\' && *args == '\\')
{
args++;
}
else if (stuff == '\"')
{
stuff = 0;
}
else if (stuff == 0)
{
args--;
}
if (argv != NULL)
{
buffer[buffplace] = stuff;
}
buffplace++;
} while (stuff);
}
else
{ // read unquoted string
const char *start = args++, *end;
while (*args && *args > ' ' && *args != '\"')
{
args++;
}
end = args;
if (argv != NULL)
{
argv[count] = buffer + buffplace;
while (start < end)
{
buffer[buffplace++] = *start++;
}
buffer[buffplace++] = 0;
}
else
{
buffplace += end - start + 1;
}
count++;
}
}
if (argc != NULL)
{
*argc = count;
}
return buffplace;
}
BOOL fluid_g_shell_parse_argv(const char *command_line, int *argcp, char ***argvp, void *_)
{
long argsize = ParseCommandLine(command_line, argcp, NULL);
*argvp = NULL;
if (*argcp != 0)
{
*argvp = (char **)malloc(*argcp * sizeof(char *) + argsize);
if (!*argvp)
{
return FALSE;
}
**argvp = (char *)*argvp + *argcp * sizeof(char *);
ParseCommandLine(command_line, NULL, *argvp);
}
return TRUE;
}
double fluid_g_get_monotonic_time(void)
{
static LARGE_INTEGER freq_cache = { 0, 0 }; /* Performance Frequency */
LARGE_INTEGER perf_cpt;
if (!freq_cache.QuadPart)
{
QueryPerformanceFrequency(&freq_cache); /* Frequency value */
}
QueryPerformanceCounter(&perf_cpt); /* Counter value */
return perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */
}
/* Thread support */
static unsigned __stdcall g_thread_wrapper(void *info_)
{
GThread *info = (GThread *)info_;
info->func(info->data);
/* Free the "GThread" now if it was detached. Otherwise, it's freed in fluid_g_thread_join. */
if (info->handle == NULL)
{
free(info);
}
_endthreadex(0);
return 0;
}
GThread *fluid_g_thread_create(GThreadFunc func, void *data, BOOL joinable, GError **error)
{
static GError error_container;
g_return_val_if_fail(func != NULL, NULL);
GThread *info = (GThread *)malloc(sizeof(GThread));
if (info == NULL)
{
return NULL;
}
info->func = func;
info->data = data;
info->handle = (HANDLE)_beginthreadex(NULL, 0, g_thread_wrapper, info, 0, NULL);
if (error != NULL)
{
error_container.code = errno;
if (errno != 0)
{
error_container.message = strerror(errno);
}
*error = &error_container;
}
if (info->handle == 0)
{
free(info);
info = NULL;
}
else if (!joinable)
{
/* Release thread reference, if caller doesn't want to join */
CloseHandle(info->handle);
info->handle = NULL;
}
return info;
}
void fluid_g_thread_join(GThread *thread)
{
if (thread != NULL && thread->handle != NULL)
{
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
free(thread);
}
}
#endif

137
src/utils/win32_glibstubs.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef _GLIBSTUBS_H
#define _GLIBSTUBS_H
#ifdef WIN32
#include <Windows.h>
#include <assert.h>
/* Miscellaneous stubs */
#define GLIB_CHECK_VERSION(x, y, z) 0 /* Evaluate to 0 to get FluidSynth to use the "old" thread API */
#define GLIB_MAJOR_VERSION 2
#define GLIB_MINOR_VERSION 29
typedef struct
{
int code;
const char *message;
} GError;
typedef void *gpointer;
#define g_new(s, c) FLUID_ARRAY(s, c)
#define g_free(p) FLUID_FREE(p)
#define g_strfreev FLUID_FREE
#define g_newa(_type, _len) (_type *)_alloca(sizeof(_type) * (_len))
#define g_assert(a) assert(a)
#define G_LIKELY(expr) (expr)
#define G_UNLIKELY(expr) (expr)
#endif
#define g_return_val_if_fail(expr, val) if (expr) {} else { return val; }
#define g_clear_error(err) do {} while (0)
#define G_FILE_TEST_EXISTS 1
#define G_FILE_TEST_IS_REGULAR 2
#define g_file_test fluid_g_file_test
#define g_shell_parse_argv fluid_g_shell_parse_argv
BOOL fluid_g_file_test(const char *path, int flags);
BOOL fluid_g_shell_parse_argv(const char *command_line, int *argcp, char ***argvp, void *dummy);
#define g_get_monotonic_time fluid_g_get_monotonic_time
double fluid_g_get_monotonic_time(void);
/* Byte ordering */
#ifdef __BYTE_ORDER__
#define G_BYTE_ORDER __BYTE_ORDER__
#define G_BIG_ENDIAN __ORDER_BIG_ENDIAN__
#else
// If __BYTE_ORDER__ isn't defined, assume little endian
#define G_BYTE_ORDER 1234
#define G_BIG_ENDIAN 4321
#endif
#if G_BYTE_ORDER == G_BIG_ENDIAN
#define GINT16_FROM_LE(x) (int16_t)(((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8))
#define GINT32_FROM_LE(x) (int32_t)((FLUID_LE16TOH(x) << 16) | (FLUID16_LE16TOH(x >> 16)))
#else
#define GINT32_FROM_LE(x) (x)
#define GINT16_FROM_LE(x) (x)
/* Thread support */
#define g_thread_supported() 1
#define g_thread_init(_) do {} while (0)
#define g_usleep(usecs) Sleep((usecs) / 1000)
typedef gpointer (*GThreadFunc)(void *data);
typedef struct
{
GThreadFunc func;
void *data;
HANDLE handle;
} GThread;
#define g_thread_create fluid_g_thread_create
#define g_thread_join fluid_g_thread_join
GThread *fluid_g_thread_create(GThreadFunc func, void *data, BOOL joinable, GError **error);
void fluid_g_thread_join(GThread *thread);
/* Regular mutex */
typedef SRWLOCK GStaticMutex;
#define G_STATIC_MUTEX_INIT SRWLOCK_INIT
#define g_static_mutex_init(_m) InitializeSRWLock(_m)
#define g_static_mutex_free(_m) do {} while (0)
#define g_static_mutex_lock(_m) AcquireSRWLockExclusive(_m)
#define g_static_mutex_unlock(_m) ReleaseSRWLockExclusive(_m)
/* Recursive lock capable mutex */
typedef CRITICAL_SECTION GStaticRecMutex;
#define g_static_rec_mutex_init(_m) InitializeCriticalSection(_m)
#define g_static_rec_mutex_free(_m) DeleteCriticalSection(_m)
#define g_static_rec_mutex_lock(_m) EnterCriticalSection(_m)
#define g_static_rec_mutex_unlock(_m) LeaveCriticalSection(_m)
/* Dynamically allocated mutex suitable for fluid_cond_t use */
typedef SRWLOCK GMutex;
#define g_mutex_free(m) do { if (m != NULL) g_free(m); } while(0)
#define g_mutex_lock(m) AcquireSRWLockExclusive(m)
#define g_mutex_unlock(m) ReleaseSRWLockExclusive(m)
static inline GMutex *g_mutex_new(void)
{
GMutex *mutex = g_new(GMutex, 1);
InitializeSRWLock(mutex);
return mutex;
}
/* Thread condition signaling */
typedef CONDITION_VARIABLE GCond;
#define g_cond_free(cond) do { if (cond != NULL) g_free(cond); } while (0)
#define g_cond_signal(cond) WakeConditionVariable(cond)
#define g_cond_broadcast(cond) WakeAllConditionVariable(cond)
#define g_cond_wait(cond, mutex) SleepConditionVariableSRW(cond, mutex, INFINITE, 0)
static inline GCond *g_cond_new(void)
{
GCond *cond = g_new(GCond, 1);
InitializeConditionVariable(cond);
return cond;
}
/* Thread private data */
typedef DWORD GStaticPrivate;
#define g_static_private_init(_priv) do { *_priv = TlsAlloc(); } while (0)
#define g_static_private_get(_priv) TlsGetValue(*_priv)
#define g_static_private_set(_priv, _data, _) TlsSetValue(*_priv, _data)
#define g_static_private_free(_priv) TlsFree(*_priv)
/* Atomic operations */
#define g_atomic_int_inc(_pi) InterlockedIncrement(_pi)
#define g_atomic_int_get(_pi) (MemoryBarrier(), *_pi)
#define g_atomic_int_set(_pi, _val) do { MemoryBarrier(); *_pi = _val; } while (0)
#define g_atomic_int_dec_and_test(_pi) (InterlockedDecrement(_pi) == 0)
#define g_atomic_int_compare_and_exchange(_pi, _old, _new) (InterlockedCompareExchange(_pi, _new, _old) == _old)
#define g_atomic_int_exchange_and_add(_pi, _add) InterlockedExchangeAdd(_pi, _add)
#endif
#endif