Path: blob/master/libs/fluidsynth/src/utils/fluid_sys.c
4396 views
/* FluidSynth - A Software Synthesizer1*2* Copyright (C) 2003 Peter Hanappe and others.3*4* This library is free software; you can redistribute it and/or5* modify it under the terms of the GNU Lesser General Public License6* as published by the Free Software Foundation; either version 2.1 of7* the License, or (at your option) any later version.8*9* This library is distributed in the hope that it will be useful, but10* WITHOUT ANY WARRANTY; without even the implied warranty of11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU12* Lesser General Public License for more details.13*14* You should have received a copy of the GNU Lesser General Public15* License along with this library; if not, write to the Free16* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA17* 02110-1301, USA18*/1920#include "fluid_sys.h"212223#if READLINE_SUPPORT24#include <readline/readline.h>25#include <readline/history.h>26#endif2728#ifdef DBUS_SUPPORT29#include "fluid_rtkit.h"30#endif3132#if HAVE_PTHREAD_H && !defined(_WIN32)33// Do not include pthread on windows. It includes winsock.h, which collides with ws2tcpip.h from fluid_sys.h34// It isn't need on Windows anyway.35#include <pthread.h>36#endif3738/* WIN32 HACK - Flag used to differentiate between a file descriptor and a socket.39* Should work, so long as no SOCKET or file descriptor ends up with this bit set. - JG */40#ifdef _WIN3241#define FLUID_SOCKET_FLAG 0x4000000042#else43#define FLUID_SOCKET_FLAG 0x0000000044#define SOCKET_ERROR -145#define INVALID_SOCKET -146#endif4748/* SCHED_FIFO priority for high priority timer threads */49#define FLUID_SYS_TIMER_HIGH_PRIO_LEVEL 10505152typedef struct53{54fluid_thread_func_t func;55void *data;56int prio_level;57} fluid_thread_info_t;5859struct _fluid_timer_t60{61long msec;6263// Pointer to a function to be executed by the timer.64// This field is set to NULL once the timer is finished to indicate completion.65// This allows for timed waits, rather than waiting forever as fluid_timer_join() does.66fluid_timer_callback_t callback;67void *data;68fluid_thread_t *thread;69int cont;70int auto_destroy;71};7273struct _fluid_server_socket_t74{75fluid_socket_t socket;76fluid_thread_t *thread;77int cont;78fluid_server_func_t func;79void *data;80};818283static int fluid_istream_gets(fluid_istream_t in, char *buf, int len);8485static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL] =86{87fluid_default_log_function,88fluid_default_log_function,89fluid_default_log_function,90fluid_default_log_function,91#ifdef DEBUG92fluid_default_log_function93#else94NULL95#endif96};97static void *fluid_log_user_data[LAST_LOG_LEVEL] = { NULL };9899static const char fluid_libname[] = "fluidsynth";100101/**102* Installs a new log function for a specified log level.103* @param level Log level to install handler for.104* @param fun Callback function handler to call for logged messages105* @param data User supplied data pointer to pass to log function106* @return The previously installed function.107*/108fluid_log_function_t109fluid_set_log_function(int level, fluid_log_function_t fun, void *data)110{111fluid_log_function_t old = NULL;112113if((level >= 0) && (level < LAST_LOG_LEVEL))114{115old = fluid_log_function[level];116fluid_log_function[level] = fun;117fluid_log_user_data[level] = data;118}119120return old;121}122123/**124* Default log function which prints to the stderr.125* @param level Log level126* @param message Log message127* @param data User supplied data (not used)128*/129void130fluid_default_log_function(int level, const char *message, void *data)131{132FILE *out;133134#if defined(_WIN32)135out = stdout;136#else137out = stderr;138#endif139140switch(level)141{142case FLUID_PANIC:143FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);144break;145146case FLUID_ERR:147FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);148break;149150case FLUID_WARN:151FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);152break;153154case FLUID_INFO:155FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);156break;157158case FLUID_DBG:159FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);160break;161162default:163FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);164break;165}166167fflush(out);168}169170/**171* Print a message to the log.172* @param level Log level (#fluid_log_level).173* @param fmt Printf style format string for log message174* @param ... Arguments for printf 'fmt' message string175* @return Always returns #FLUID_FAILED176*/177int178fluid_log(int level, const char *fmt, ...)179{180if((level >= 0) && (level < LAST_LOG_LEVEL))181{182fluid_log_function_t fun = fluid_log_function[level];183184if(fun != NULL)185{186char errbuf[1024];187188va_list args;189va_start(args, fmt);190FLUID_VSNPRINTF(errbuf, sizeof(errbuf), fmt, args);191va_end(args);192193(*fun)(level, errbuf, fluid_log_user_data[level]);194}195}196197return FLUID_FAILED;198}199200void* fluid_alloc(size_t len)201{202void* ptr = malloc(len);203204#if defined(DEBUG) && !defined(_MSC_VER)205// garbage initialize allocated memory for debug builds to ease reproducing206// bugs like 44453ff23281b3318abbe432fda90888c373022b .207//208// MSVC++ already garbage initializes allocated memory by itself (debug-heap).209//210// 0xCC because211// * it makes pointers reliably crash when dereferencing them,212// * floating points are still some valid but insanely huge negative number, and213// * if for whatever reason this allocated memory is executed, it'll trigger214// INT3 (...at least on x86)215if(ptr != NULL)216{217memset(ptr, 0xCC, len);218}219#endif220return ptr;221}222223/**224* Open a file with a UTF-8 string, even in Windows225* @param filename The name of the file to open226* @param mode The mode to open the file in227*/228FILE *fluid_fopen(const char *filename, const char *mode)229{230#if defined(_WIN32)231wchar_t *wpath = NULL, *wmode = NULL;232FILE *file = NULL;233int length;234if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, NULL, 0)) == 0)235{236FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for filename '%s'. Error was: '%s'", filename, fluid_get_windows_error());237errno = EINVAL;238goto error_recovery;239}240241wpath = FLUID_MALLOC(length * sizeof(wchar_t));242if (wpath == NULL)243{244FLUID_LOG(FLUID_PANIC, "Out of memory.");245errno = EINVAL;246goto error_recovery;247}248249MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, wpath, length);250251if ((length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, NULL, 0)) == 0)252{253FLUID_LOG(FLUID_ERR, "Unable to perform MultiByteToWideChar() conversion for mode '%s'. Error was: '%s'", mode, fluid_get_windows_error());254errno = EINVAL;255goto error_recovery;256}257258wmode = FLUID_MALLOC(length * sizeof(wchar_t));259if (wmode == NULL)260{261FLUID_LOG(FLUID_PANIC, "Out of memory.");262errno = EINVAL;263goto error_recovery;264}265266MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mode, -1, wmode, length);267268file = _wfopen(wpath, wmode);269270error_recovery:271FLUID_FREE(wpath);272FLUID_FREE(wmode);273274return file;275#else276return fopen(filename, mode);277#endif278}279280/**281* Wrapper for free() that satisfies at least C90 requirements.282*283* @param ptr Pointer to memory region that should be freed284*285* @note Only use this function when the API documentation explicitly says so. Otherwise use286* adequate \c delete_fluid_* functions.287*288* @warning Calling ::free() on memory that is advised to be freed with fluid_free() results in undefined behaviour!289* (cf.: "Potential Errors Passing CRT Objects Across DLL Boundaries" found in MS Docs)290*291* @since 2.0.7292*/293void fluid_free(void* ptr)294{295free(ptr);296}297298/**299* An improved strtok, still trashes the input string, but is portable and300* thread safe. Also skips token chars at beginning of token string and never301* returns an empty token (will return NULL if source ends in token chars though).302* NOTE: NOT part of public API303* @internal304* @param str Pointer to a string pointer of source to tokenize. Pointer gets305* updated on each invocation to point to beginning of next token. Note that306* token char gets overwritten with a 0 byte. String pointer is set to NULL307* when final token is returned.308* @param delim String of delimiter chars.309* @return Pointer to the next token or NULL if no more tokens.310*/311char *fluid_strtok(char **str, char *delim)312{313char *s, *d, *token;314char c;315316if(str == NULL || delim == NULL || !*delim)317{318FLUID_LOG(FLUID_ERR, "Null pointer");319return NULL;320}321322s = *str;323324if(!s)325{326return NULL; /* str points to a NULL pointer? (tokenize already ended) */327}328329/* skip delimiter chars at beginning of token */330do331{332c = *s;333334if(!c) /* end of source string? */335{336*str = NULL;337return NULL;338}339340for(d = delim; *d; d++) /* is source char a token char? */341{342if(c == *d) /* token char match? */343{344s++; /* advance to next source char */345break;346}347}348}349while(*d); /* while token char match */350351token = s; /* start of token found */352353/* search for next token char or end of source string */354for(s = s + 1; *s; s++)355{356c = *s;357358for(d = delim; *d; d++) /* is source char a token char? */359{360if(c == *d) /* token char match? */361{362*s = '\0'; /* overwrite token char with zero byte to terminate token */363*str = s + 1; /* update str to point to beginning of next token */364return token;365}366}367}368369/* we get here only if source string ended */370*str = NULL;371return token;372}373374/**375* Suspend the execution of the current thread for the specified amount of time.376* @param milliseconds to wait.377*/378void fluid_msleep(unsigned int msecs)379{380g_usleep(msecs * 1000);381}382383/**384* Get time in milliseconds to be used in relative timing operations.385* @return Monotonic time in milliseconds.386*/387unsigned int fluid_curtime(void)388{389double now;390static double initial_time = 0;391392if(initial_time == 0)393{394initial_time = fluid_utime();395}396397now = fluid_utime();398399return (unsigned int)((now - initial_time) / 1000.0);400}401402/**403* Get time in microseconds to be used in relative timing operations.404* @return time in microseconds.405* Note: When used for profiling we need high precision clock given406* by g_get_monotonic_time()if available (glib version >= 2.53.3).407* If glib version is too old and in the case of Windows the function408* uses high precision performance counter instead of g_getmonotic_time().409*/410double411fluid_utime(void)412{413double utime;414415#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 28416/* use high precision monotonic clock if available (g_monotonic_time().417* For Windows, if this clock is actually implemented as low prec. clock418* (i.e. in case glib is too old), high precision performance counter are419* used instead.420* see: https://bugzilla.gnome.org/show_bug.cgi?id=783340421*/422#if defined(WITH_PROFILING) && defined(_WIN32) &&\423/* glib < 2.53.3 */\424(GLIB_MINOR_VERSION <= 53 && (GLIB_MINOR_VERSION < 53 || GLIB_MICRO_VERSION < 3))425/* use high precision performance counter. */426static LARGE_INTEGER freq_cache = {0, 0}; /* Performance Frequency */427LARGE_INTEGER perf_cpt;428429if(! freq_cache.QuadPart)430{431QueryPerformanceFrequency(&freq_cache); /* Frequency value */432}433434QueryPerformanceCounter(&perf_cpt); /* Counter value */435utime = perf_cpt.QuadPart * 1000000.0 / freq_cache.QuadPart; /* time in micros */436#else437utime = g_get_monotonic_time();438#endif439#else440/* fallback to less precise clock */441GTimeVal timeval;442g_get_current_time(&timeval);443utime = (timeval.tv_sec * 1000000.0 + timeval.tv_usec);444#endif445446return utime;447}448449450451#if defined(_WIN32) /* Windoze specific stuff */452453void454fluid_thread_self_set_prio(int prio_level)455{456if(prio_level > 0)457{458SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);459}460}461462463#elif defined(__OS2__) /* OS/2 specific stuff */464465void466fluid_thread_self_set_prio(int prio_level)467{468if(prio_level > 0)469{470DosSetPriority(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0);471}472}473474#else /* POSIX stuff.. Nice POSIX.. Good POSIX. */475476void477fluid_thread_self_set_prio(int prio_level)478{479struct sched_param priority;480481if(prio_level > 0)482{483484memset(&priority, 0, sizeof(priority));485priority.sched_priority = prio_level;486487if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &priority) == 0)488{489return;490}491492#ifdef DBUS_SUPPORT493/* Try to gain high priority via rtkit */494495if(fluid_rtkit_make_realtime(0, prio_level) == 0)496{497return;498}499500#endif501FLUID_LOG(FLUID_WARN, "Failed to set thread to high priority");502}503}504505#ifdef FPE_CHECK506507/***************************************************************508*509* Floating point exceptions510*511* The floating point exception functions were taken from Ircam's512* jMax source code. https://www.ircam.fr/jmax513*514* FIXME: check in config for i386 machine515*516* Currently not used. I leave the code here in case we want to pick517* this up again some time later.518*/519520/* Exception flags */521#define _FPU_STATUS_IE 0x001 /* Invalid Operation */522#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */523#define _FPU_STATUS_ZE 0x004 /* Zero Divide */524#define _FPU_STATUS_OE 0x008 /* Overflow */525#define _FPU_STATUS_UE 0x010 /* Underflow */526#define _FPU_STATUS_PE 0x020 /* Precision */527#define _FPU_STATUS_SF 0x040 /* Stack Fault */528#define _FPU_STATUS_ES 0x080 /* Error Summary Status */529530/* Macros for accessing the FPU status word. */531532/* get the FPU status */533#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))534535/* clear the FPU status */536#define _FPU_CLR_SW() __asm__ ("fnclex" : : )537538/* Purpose:539* Checks, if the floating point unit has produced an exception, print a message540* if so and clear the exception.541*/542unsigned int fluid_check_fpe_i386(char *explanation)543{544unsigned int s;545546_FPU_GET_SW(s);547_FPU_CLR_SW();548549s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;550551if(s)552{553FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,554(s & _FPU_STATUS_IE) ? "Invalid operation " : "",555(s & _FPU_STATUS_DE) ? "Denormal number " : "",556(s & _FPU_STATUS_ZE) ? "Zero divide " : "",557(s & _FPU_STATUS_OE) ? "Overflow " : "",558(s & _FPU_STATUS_UE) ? "Underflow " : "");559}560561return s;562}563564/* Purpose:565* Clear floating point exception.566*/567void fluid_clear_fpe_i386(void)568{569_FPU_CLR_SW();570}571572#endif // ifdef FPE_CHECK573574575#endif // #else (its POSIX)576577578/***************************************************************579*580* Profiling (Linux, i586 only)581*582*/583584#if WITH_PROFILING585/* Profiling interface between profiling command shell and audio rendering API586(FluidProfile_0004.pdf- 3.2.2).587Macros are in defined in fluid_sys.h.588*/589590/*591-----------------------------------------------------------------------------592Shell task side | Profiling interface | Audio task side593-----------------------------------------------------------------------------594profiling | Internal | | | Audio595command <---> |<-- profiling -->| Data |<--macros -->| <--> rendering596shell | API | | | API597598*/599/* default parameters for shell command "prof_start" in fluid_sys.c */600unsigned short fluid_profile_notes = 0; /* number of generated notes */601/* preset bank:0 prog:16 (organ) */602unsigned char fluid_profile_bank = FLUID_PROFILE_DEFAULT_BANK;603unsigned char fluid_profile_prog = FLUID_PROFILE_DEFAULT_PROG;604605/* print mode */606unsigned char fluid_profile_print = FLUID_PROFILE_DEFAULT_PRINT;607/* number of measures */608unsigned short fluid_profile_n_prof = FLUID_PROFILE_DEFAULT_N_PROF;609/* measure duration in ms */610unsigned short fluid_profile_dur = FLUID_PROFILE_DEFAULT_DURATION;611/* lock between multiple-shell */612fluid_atomic_int_t fluid_profile_lock = 0;613/**/614615/*----------------------------------------------616Profiling Data617-----------------------------------------------*/618unsigned char fluid_profile_status = PROFILE_STOP; /* command and status */619unsigned int fluid_profile_end_ticks = 0; /* ending position (in ticks) */620fluid_profile_data_t fluid_profile_data[] = /* Data duration */621{622{"synth_write_* ------------>", 1e10, 0.0, 0.0, 0, 0, 0},623{"synth_one_block ---------->", 1e10, 0.0, 0.0, 0, 0, 0},624{"synth_one_block:clear ---->", 1e10, 0.0, 0.0, 0, 0, 0},625{"synth_one_block:one voice->", 1e10, 0.0, 0.0, 0, 0, 0},626{"synth_one_block:all voices>", 1e10, 0.0, 0.0, 0, 0, 0},627{"synth_one_block:reverb --->", 1e10, 0.0, 0.0, 0, 0, 0},628{"synth_one_block:chorus --->", 1e10, 0.0, 0.0, 0, 0, 0},629{"voice:note --------------->", 1e10, 0.0, 0.0, 0, 0, 0},630{"voice:release ------------>", 1e10, 0.0, 0.0, 0, 0, 0}631};632633634/*----------------------------------------------635Internal profiling API636-----------------------------------------------*/637/* logging profiling data (used on synthesizer instance deletion) */638void fluid_profiling_print(void)639{640int i;641642FLUID_LOG(FLUID_INFO, "fluid_profiling_print\n");643644FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");645646for(i = 0; i < FLUID_PROFILE_NBR; i++)647{648if(fluid_profile_data[i].count > 0)649{650FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",651fluid_profile_data[i].description,652fluid_profile_data[i].min,653fluid_profile_data[i].total / fluid_profile_data[i].count,654fluid_profile_data[i].max);655}656else657{658FLUID_LOG(FLUID_DBG, "%s: no profiling available",659fluid_profile_data[i].description);660}661}662}663664/* Macro that returns cpu load in percent (%)665* @dur: duration (micro second).666* @sample_rate: sample_rate used in audio driver (Hz).667* @n_amples: number of samples collected during 'dur' duration.668*/669#define fluid_profile_load(dur,sample_rate,n_samples) \670(dur * sample_rate / n_samples / 10000.0)671672673/* prints cpu loads only674*675* @param sample_rate the sample rate of audio output.676* @param out output stream device.677*678* ------------------------------------------------------------------------------679* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices680* ------------------------------------------------------------------------------681* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices682* -------|---------|---------|----------|---------|---------|-------------------683* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612684*/685static void fluid_profiling_print_load(double sample_rate, fluid_ostream_t out)686{687unsigned int n_voices; /* voices number */688static const char max_voices_not_available[] = " not available";689const char *pmax_voices;690char max_voices_available[20];691692/* First computes data to be printed */693double total, voices, reverb, chorus, all_voices, voice;694/* voices number */695n_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?696fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_voices /697fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count : 0;698699/* total load (%) */700total = fluid_profile_data[FLUID_PROF_WRITE].count ?701fluid_profile_load(fluid_profile_data[FLUID_PROF_WRITE].total, sample_rate,702fluid_profile_data[FLUID_PROF_WRITE].n_samples) : 0;703704/* reverb load (%) */705reverb = fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].count ?706fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].total,707sample_rate,708fluid_profile_data[FLUID_PROF_ONE_BLOCK_REVERB].n_samples) : 0;709710/* chorus load (%) */711chorus = fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].count ?712fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].total,713sample_rate,714fluid_profile_data[FLUID_PROF_ONE_BLOCK_CHORUS].n_samples) : 0;715716/* total voices load: total - reverb - chorus (%) */717voices = total - reverb - chorus;718719/* One voice load (%): all_voices / n_voices. */720all_voices = fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].count ?721fluid_profile_load(fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].total,722sample_rate,723fluid_profile_data[FLUID_PROF_ONE_BLOCK_VOICES].n_samples) : 0;724725voice = n_voices ? all_voices / n_voices : 0;726727/* estimated maximum voices number */728if(voice > 0)729{730FLUID_SNPRINTF(max_voices_available, sizeof(max_voices_available),731"%17d", (unsigned int)((100.0 - reverb - chorus) / voice));732pmax_voices = max_voices_available;733}734else735{736pmax_voices = max_voices_not_available;737}738739/* Now prints data */740fluid_ostream_printf(out,741" ------------------------------------------------------------------------------\n");742fluid_ostream_printf(out,743" Cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond) and maximum voices\n",744sample_rate, 1000000.0 / sample_rate);745fluid_ostream_printf(out,746" ------------------------------------------------------------------------------\n");747fluid_ostream_printf(out,748" nVoices| total(%%)|voices(%%)| reverb(%%)|chorus(%%)| voice(%%)|estimated maxVoices\n");749fluid_ostream_printf(out,750" -------|---------|---------|----------|---------|---------|-------------------\n");751fluid_ostream_printf(out,752"%8d|%9.3f|%9.3f|%10.3f|%9.3f|%9.3f|%s\n", n_voices, total, voices,753reverb, chorus, voice, pmax_voices);754}755756/*757* prints profiling data (used by profile shell command: prof_start).758* The function is an internal profiling API between the "profile" command759* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).760*761* @param sample_rate the sample rate of audio output.762* @param out output stream device.763*764* When print mode is 1, the function prints all the information (see below).765* When print mode is 0, the function prints only the cpu loads.766*767* ------------------------------------------------------------------------------768* Duration(microsecond) and cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond)769* ------------------------------------------------------------------------------770* Code under profiling |Voices| Duration (microsecond) | Load(%)771* | nbr| min| avg| max|772* ---------------------------|------|--------------------------------|----------773* synth_write_* ------------>| 250| 3.91| 2188.82| 3275.00| 41.544774* synth_one_block ---------->| 250| 1150.70| 2273.56| 3241.47| 41.100775* synth_one_block:clear ---->| 250| 3.07| 4.62| 61.18| 0.084776* synth_one_block:one voice->| 1| 4.19| 9.02| 1044.27| 0.163777* synth_one_block:all voices>| 250| 1138.41| 2259.11| 3217.73| 40.839778* synth_one_block:reverb --->| no profiling available779* synth_one_block:chorus --->| no profiling available780* voice:note --------------->| no profiling available781* voice:release ------------>| no profiling available782* ------------------------------------------------------------------------------783* Cpu loads(%) (sr: 44100 Hz, sp: 22.68 microsecond) and maximum voices784* ------------------------------------------------------------------------------785* nVoices| total(%)|voices(%)| reverb(%)|chorus(%)| voice(%)|estimated maxVoices786* -------|---------|---------|----------|---------|---------|-------------------787* 250| 41.544| 41.544| 0.000| 0.000| 0.163| 612788*/789void fluid_profiling_print_data(double sample_rate, fluid_ostream_t out)790{791int i;792793if(fluid_profile_print)794{795/* print all details: Duration(microsecond) and cpu loads(%) */796fluid_ostream_printf(out,797" ------------------------------------------------------------------------------\n");798fluid_ostream_printf(out,799" Duration(microsecond) and cpu loads(%%) (sr:%6.0f Hz, sp:%6.2f microsecond)\n",800sample_rate, 1000000.0 / sample_rate);801fluid_ostream_printf(out,802" ------------------------------------------------------------------------------\n");803fluid_ostream_printf(out,804" Code under profiling |Voices| Duration (microsecond) | Load(%%)\n");805fluid_ostream_printf(out,806" | nbr| min| avg| max|\n");807fluid_ostream_printf(out,808" ---------------------------|------|--------------------------------|----------\n");809810for(i = 0; i < FLUID_PROFILE_NBR; i++)811{812unsigned int count = fluid_profile_data[i].count;813814if(count > 0)815{816/* data are available */817818if(FLUID_PROF_WRITE <= i && i <= FLUID_PROF_ONE_BLOCK_CHORUS)819{820double load = fluid_profile_load(fluid_profile_data[i].total, sample_rate,821fluid_profile_data[i].n_samples);822fluid_ostream_printf(out, " %s|%6d|%10.2f|%10.2f|%10.2f|%8.3f\n",823fluid_profile_data[i].description, /* code under profiling */824fluid_profile_data[i].n_voices / count, /* voices number */825fluid_profile_data[i].min, /* minimum duration */826fluid_profile_data[i].total / count, /* average duration */827fluid_profile_data[i].max, /* maximum duration */828load); /* cpu load */829}830else831{832/* note and release duration */833fluid_ostream_printf(out, " %s|%6d|%10.0f|%10.0f|%10.0f|\n",834fluid_profile_data[i].description, /* code under profiling */835fluid_profile_data[i].n_voices / count,836fluid_profile_data[i].min, /* minimum duration */837fluid_profile_data[i].total / count, /* average duration */838fluid_profile_data[i].max); /* maximum duration */839}840}841else842{843/* data aren't available */844fluid_ostream_printf(out,845" %s| no profiling available\n", fluid_profile_data[i].description);846}847}848}849850/* prints cpu loads only */851fluid_profiling_print_load(sample_rate, out);/* prints cpu loads */852}853854/*855Returns true if the user cancels the current profiling measurement.856Actually this is implemented using the <ENTER> key. To add this functionality:8571) Adds #define FLUID_PROFILE_CANCEL in fluid_sys.h.8582) Adds the necessary code inside fluid_profile_is_cancel().859860When FLUID_PROFILE_CANCEL is not defined, the function return FALSE.861*/862int fluid_profile_is_cancel_req(void)863{864#ifdef FLUID_PROFILE_CANCEL865866#if defined(_WIN32) /* Windows specific stuff */867/* Profile cancellation is supported for Windows */868/* returns TRUE if key <ENTER> is depressed */869return(GetAsyncKeyState(VK_RETURN) & 0x1);870871#elif defined(__OS2__) /* OS/2 specific stuff */872/* Profile cancellation isn't yet supported for OS2 */873/* For OS2, replaces the following line with the function that returns874true when the keyboard key <ENTER> is depressed */875return FALSE; /* default value */876877#else /* POSIX stuff */878/* Profile cancellation is supported for Linux */879/* returns true is <ENTER> is depressed */880{881/* Here select() is used to poll the standard input to see if an input882is ready. As the standard input is usually buffered, the user883needs to depress <ENTER> to set the input to a "ready" state.884*/885struct timeval tv;886fd_set fds; /* just one fds need to be polled */887tv.tv_sec = 0; /* Setting both values to 0, means a 0 timeout */888tv.tv_usec = 0;889FD_ZERO(&fds); /* reset fds */890FD_SET(STDIN_FILENO, &fds); /* sets fds to poll standard input only */891select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); /* polling */892return (FD_ISSET(0, &fds)); /* returns true if standard input is ready */893}894#endif /* OS stuff */895896#else /* FLUID_PROFILE_CANCEL not defined */897return FALSE; /* default value */898#endif /* FLUID_PROFILE_CANCEL */899}900901/**902* Returns status used in shell command "prof_start".903* The function is an internal profiling API between the "profile" command904* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).905*906* @return status907* - PROFILE_READY profiling data are ready.908* - PROFILE_RUNNING, profiling data are still under acquisition.909* - PROFILE_CANCELED, acquisition has been cancelled by the user.910* - PROFILE_STOP, no acquisition in progress.911*912* When status is PROFILE_RUNNING, the caller can do passive waiting, or other913* work before recalling the function later.914*/915int fluid_profile_get_status(void)916{917/* Checks if user has requested to cancel the current measurement */918/* Cancellation must have precedence over other status */919if(fluid_profile_is_cancel_req())920{921fluid_profile_start_stop(0, 0); /* stops the measurement */922return PROFILE_CANCELED;923}924925switch(fluid_profile_status)926{927case PROFILE_READY:928return PROFILE_READY; /* profiling data are ready */929930case PROFILE_START:931return PROFILE_RUNNING;/* profiling data are under acquisition */932933default:934return PROFILE_STOP;935}936}937938/**939* Starts or stops profiling measurement.940* The function is an internal profiling API between the "profile" command941* prof_start and audio rendering API (see FluidProfile_0004.pdf - 3.2.2).942*943* @param end_tick end position of the measure (in ticks).944* - If end_tick is greater then 0, the function starts a measure if a measure945* isn't running. If a measure is already running, the function does nothing946* and returns.947* - If end_tick is 0, the function stops a measure.948* @param clear_data,949* - If clear_data is 0, the function clears fluid_profile_data before starting950* a measure, otherwise, the data from the started measure will be accumulated951* within fluid_profile_data.952*/953void fluid_profile_start_stop(unsigned int end_ticks, short clear_data)954{955if(end_ticks) /* This is a "start" request */956{957/* Checks if a measure is already running */958if(fluid_profile_status != PROFILE_START)959{960short i;961fluid_profile_end_ticks = end_ticks;962963/* Clears profile data */964if(clear_data == 0)965{966for(i = 0; i < FLUID_PROFILE_NBR; i++)967{968fluid_profile_data[i].min = 1e10;/* min sets to max value */969fluid_profile_data[i].max = 0; /* maximum sets to min value */970fluid_profile_data[i].total = 0; /* total duration microsecond */971fluid_profile_data[i].count = 0; /* data count */972fluid_profile_data[i].n_voices = 0; /* voices number */973fluid_profile_data[i].n_samples = 0;/* audio samples number */974}975}976977fluid_profile_status = PROFILE_START; /* starts profiling */978}979980/* else do nothing when profiling is already started */981}982else /* This is a "stop" request */983{984/* forces the current running profile (if any) to stop */985fluid_profile_status = PROFILE_STOP; /* stops profiling */986}987}988989#endif /* WITH_PROFILING */990991/***************************************************************992*993* Threads994*995*/996997#if OLD_GLIB_THREAD_API998999/* Rather than inline this one, we just declare it as a function, to prevent1000* GCC warning about inline failure. */1001fluid_cond_t *1002new_fluid_cond(void)1003{1004if(!g_thread_supported())1005{1006g_thread_init(NULL);1007}10081009return g_cond_new();1010}10111012#endif10131014static gpointer1015fluid_thread_high_prio(gpointer data)1016{1017fluid_thread_info_t *info = data;10181019fluid_thread_self_set_prio(info->prio_level);10201021info->func(info->data);1022FLUID_FREE(info);10231024return NULL;1025}10261027/**1028* Create a new thread.1029* @param func Function to execute in new thread context1030* @param data User defined data to pass to func1031* @param prio_level Priority level. If greater than 0 then high priority scheduling will1032* be used, with the given priority level (used by pthreads only). 0 uses normal scheduling.1033* @param detach If TRUE, 'join' does not work and the thread destroys itself when finished.1034* @return New thread pointer or NULL on error1035*/1036fluid_thread_t *1037new_fluid_thread(const char *name, fluid_thread_func_t func, void *data, int prio_level, int detach)1038{1039GThread *thread;1040fluid_thread_info_t *info = NULL;1041GError *err = NULL;10421043g_return_val_if_fail(func != NULL, NULL);10441045#if OLD_GLIB_THREAD_API10461047/* Make sure g_thread_init has been called.1048* Probably not a good idea in a shared library,1049* but what can we do *and* remain backwards compatible? */1050if(!g_thread_supported())1051{1052g_thread_init(NULL);1053}10541055#endif10561057if(prio_level > 0)1058{1059info = FLUID_NEW(fluid_thread_info_t);10601061if(!info)1062{1063FLUID_LOG(FLUID_ERR, "Out of memory");1064return NULL;1065}10661067info->func = func;1068info->data = data;1069info->prio_level = prio_level;1070#if NEW_GLIB_THREAD_API1071thread = g_thread_try_new(name, fluid_thread_high_prio, info, &err);1072#else1073thread = g_thread_create(fluid_thread_high_prio, info, detach == FALSE, &err);1074#endif1075}10761077else1078{1079#if NEW_GLIB_THREAD_API1080thread = g_thread_try_new(name, (GThreadFunc)func, data, &err);1081#else1082thread = g_thread_create((GThreadFunc)func, data, detach == FALSE, &err);1083#endif1084}10851086if(!thread)1087{1088FLUID_LOG(FLUID_ERR, "Failed to create the thread: %s",1089fluid_gerror_message(err));1090g_clear_error(&err);1091FLUID_FREE(info);1092return NULL;1093}10941095#if NEW_GLIB_THREAD_API10961097if(detach)1098{1099g_thread_unref(thread); // Release thread reference, if caller wants to detach1100}11011102#endif11031104return thread;1105}11061107/**1108* Frees data associated with a thread (does not actually stop thread).1109* @param thread Thread to free1110*/1111void1112delete_fluid_thread(fluid_thread_t *thread)1113{1114/* Threads free themselves when they quit, nothing to do */1115}11161117/**1118* Join a thread (wait for it to terminate).1119* @param thread Thread to join1120* @return FLUID_OK1121*/1122int1123fluid_thread_join(fluid_thread_t *thread)1124{1125g_thread_join(thread);11261127return FLUID_OK;1128}112911301131static fluid_thread_return_t1132fluid_timer_run(void *data)1133{1134fluid_timer_t *timer;1135int count = 0;1136int cont;1137long start;1138long delay;11391140timer = (fluid_timer_t *)data;11411142/* keep track of the start time for absolute positioning */1143start = fluid_curtime();11441145while(timer->cont)1146{1147cont = (*timer->callback)(timer->data, fluid_curtime() - start);11481149count++;11501151if(!cont)1152{1153break;1154}11551156/* to avoid incremental time errors, calculate the delay between1157two callbacks bringing in the "absolute" time (count *1158timer->msec) */1159delay = (count * timer->msec) - (fluid_curtime() - start);11601161if(delay > 0)1162{1163fluid_msleep(delay);1164}1165}11661167FLUID_LOG(FLUID_DBG, "Timer thread finished");1168timer->callback = NULL;11691170if(timer->auto_destroy)1171{1172FLUID_FREE(timer);1173}11741175return FLUID_THREAD_RETURN_VALUE;1176}11771178fluid_timer_t *1179new_fluid_timer(int msec, fluid_timer_callback_t callback, void *data,1180int new_thread, int auto_destroy, int high_priority)1181{1182fluid_timer_t *timer;11831184timer = FLUID_NEW(fluid_timer_t);11851186if(timer == NULL)1187{1188FLUID_LOG(FLUID_ERR, "Out of memory");1189return NULL;1190}11911192timer->msec = msec;1193timer->callback = callback;1194timer->data = data;1195timer->cont = TRUE ;1196timer->thread = NULL;1197timer->auto_destroy = auto_destroy;11981199if(new_thread)1200{1201timer->thread = new_fluid_thread("timer", fluid_timer_run, timer, high_priority1202? FLUID_SYS_TIMER_HIGH_PRIO_LEVEL : 0, FALSE);12031204if(!timer->thread)1205{1206FLUID_FREE(timer);1207return NULL;1208}1209}1210else1211{1212fluid_timer_run(timer); /* Run directly, instead of as a separate thread */12131214if(auto_destroy)1215{1216/* do NOT return freed memory */1217return NULL;1218}1219}12201221return timer;1222}12231224void1225delete_fluid_timer(fluid_timer_t *timer)1226{1227int auto_destroy;1228fluid_return_if_fail(timer != NULL);12291230auto_destroy = timer->auto_destroy;12311232timer->cont = 0;1233fluid_timer_join(timer);12341235/* Shouldn't access timer now if auto_destroy enabled, since it has been destroyed */12361237if(!auto_destroy)1238{1239FLUID_FREE(timer);1240}1241}12421243int1244fluid_timer_join(fluid_timer_t *timer)1245{1246int auto_destroy;12471248if(timer->thread)1249{1250auto_destroy = timer->auto_destroy;1251fluid_thread_join(timer->thread);12521253if(!auto_destroy)1254{1255timer->thread = NULL;1256}1257}12581259return FLUID_OK;1260}12611262int1263fluid_timer_is_running(const fluid_timer_t *timer)1264{1265// for unit test usage only1266return timer->callback != NULL;1267}12681269long fluid_timer_get_interval(const fluid_timer_t * timer)1270{1271// for unit test usage only1272return timer->msec;1273}127412751276/***************************************************************1277*1278* Sockets and I/O1279*1280*/12811282/**1283* Get standard in stream handle.1284* @return Standard in stream.1285*/1286fluid_istream_t1287fluid_get_stdin(void)1288{1289return STDIN_FILENO;1290}12911292/**1293* Get standard output stream handle.1294* @return Standard out stream.1295*/1296fluid_ostream_t1297fluid_get_stdout(void)1298{1299return STDOUT_FILENO;1300}13011302/**1303* Read a line from an input stream.1304* @return 0 if end-of-stream, -1 if error, non zero otherwise1305*/1306int1307fluid_istream_readline(fluid_istream_t in, fluid_ostream_t out, char *prompt,1308char *buf, int len)1309{1310#if READLINE_SUPPORT13111312if(in == fluid_get_stdin())1313{1314char *line;13151316line = readline(prompt);13171318if(line == NULL)1319{1320return -1;1321}13221323FLUID_SNPRINTF(buf, len, "%s", line);1324buf[len - 1] = 0;13251326if(buf[0] != '\0')1327{1328add_history(buf);1329}13301331free(line);1332return 1;1333}1334else1335#endif1336{1337fluid_ostream_printf(out, "%s", prompt);1338return fluid_istream_gets(in, buf, len);1339}1340}13411342/**1343* Reads a line from an input stream (socket).1344* @param in The input socket1345* @param buf Buffer to store data to1346* @param len Maximum length to store to buf1347* @return 1 if a line was read, 0 on end of stream, -1 on error1348*/1349static int1350fluid_istream_gets(fluid_istream_t in, char *buf, int len)1351{1352char c;1353int n;13541355buf[len - 1] = 0;13561357while(--len > 0)1358{1359#ifndef _WIN321360n = read(in, &c, 1);13611362if(n == -1)1363{1364return -1;1365}13661367#else13681369/* Handle read differently depending on if its a socket or file descriptor */1370if(!(in & FLUID_SOCKET_FLAG))1371{1372// usually read() is supposed to return '\n' as last valid character of the user input1373// when compiled with compatibility for WinXP however, read() may return 0 (EOF) rather than '\n'1374// this would cause the shell to exit early1375n = read(in, &c, 1);13761377if(n == -1)1378{1379return -1;1380}1381}1382else1383{1384#ifdef NETWORK_SUPPORT1385n = recv(in & ~FLUID_SOCKET_FLAG, &c, 1, 0);1386if(n == SOCKET_ERROR)1387#endif1388{1389return -1;1390}1391}13921393#endif13941395if(n == 0)1396{1397*buf = 0;1398// return 1 if read from stdin, else 0, to fix early exit of shell1399return (in == STDIN_FILENO);1400}14011402if(c == '\n')1403{1404*buf = 0;1405return 1;1406}14071408/* Store all characters excluding CR */1409if(c != '\r')1410{1411*buf++ = c;1412}1413}14141415return -1;1416}14171418/**1419* Send a printf style string with arguments to an output stream (socket).1420* @param out Output stream1421* @param format printf style format string1422* @param ... Arguments for the printf format string1423* @return Number of bytes written or -1 on error1424*/1425int1426fluid_ostream_printf(fluid_ostream_t out, const char *format, ...)1427{1428char buf[4096];1429va_list args;1430int len;14311432va_start(args, format);1433len = FLUID_VSNPRINTF(buf, 4095, format, args);1434va_end(args);14351436if(len == 0)1437{1438return 0;1439}14401441if(len < 0)1442{1443printf("fluid_ostream_printf: buffer overflow");1444return -1;1445}14461447buf[4095] = 0;14481449#ifndef _WIN321450return write(out, buf, FLUID_STRLEN(buf));1451#else1452{1453int retval;14541455/* Handle write differently depending on if its a socket or file descriptor */1456if(!(out & FLUID_SOCKET_FLAG))1457{1458return write(out, buf, (unsigned int)FLUID_STRLEN(buf));1459}14601461#ifdef NETWORK_SUPPORT1462/* Socket */1463retval = send(out & ~FLUID_SOCKET_FLAG, buf, (int)FLUID_STRLEN(buf), 0);1464return retval != SOCKET_ERROR ? retval : -1;1465#else1466return -1;1467#endif1468}1469#endif1470}14711472#ifdef NETWORK_SUPPORT14731474int fluid_server_socket_join(fluid_server_socket_t *server_socket)1475{1476return fluid_thread_join(server_socket->thread);1477}14781479static int fluid_socket_init(void)1480{1481#ifdef _WIN321482WSADATA wsaData;1483int res = WSAStartup(MAKEWORD(2, 2), &wsaData);14841485if(res != 0)1486{1487FLUID_LOG(FLUID_ERR, "Server socket creation error: WSAStartup failed: %d", res);1488return FLUID_FAILED;1489}14901491#endif14921493return FLUID_OK;1494}14951496static void fluid_socket_cleanup(void)1497{1498#ifdef _WIN321499WSACleanup();1500#endif1501}15021503static int fluid_socket_get_error(void)1504{1505#ifdef _WIN321506return (int)WSAGetLastError();1507#else1508return errno;1509#endif1510}15111512fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock)1513{1514return sock | FLUID_SOCKET_FLAG;1515}15161517fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock)1518{1519return sock | FLUID_SOCKET_FLAG;1520}15211522void fluid_socket_close(fluid_socket_t sock)1523{1524if(sock != INVALID_SOCKET)1525{1526#ifdef _WIN321527closesocket(sock);15281529#else1530close(sock);1531#endif1532}1533}15341535static fluid_thread_return_t fluid_server_socket_run(void *data)1536{1537fluid_server_socket_t *server_socket = (fluid_server_socket_t *)data;1538fluid_socket_t client_socket;1539#ifdef IPV6_SUPPORT1540struct sockaddr_in6 addr;1541#else1542struct sockaddr_in addr;1543#endif15441545#ifdef HAVE_INETNTOP1546#ifdef IPV6_SUPPORT1547char straddr[INET6_ADDRSTRLEN];1548#else1549char straddr[INET_ADDRSTRLEN];1550#endif /* IPV6_SUPPORT */1551#endif /* HAVE_INETNTOP */15521553socklen_t addrlen = sizeof(addr);1554int r;1555FLUID_MEMSET((char *)&addr, 0, sizeof(addr));15561557FLUID_LOG(FLUID_DBG, "Server listening for connections");15581559while(server_socket->cont)1560{1561client_socket = accept(server_socket->socket, (struct sockaddr *)&addr, &addrlen);15621563FLUID_LOG(FLUID_DBG, "New client connection");15641565if(client_socket == INVALID_SOCKET)1566{1567if(server_socket->cont)1568{1569FLUID_LOG(FLUID_ERR, "Failed to accept connection: %d", fluid_socket_get_error());1570}15711572server_socket->cont = 0;1573return FLUID_THREAD_RETURN_VALUE;1574}1575else1576{1577#ifdef HAVE_INETNTOP15781579#ifdef IPV6_SUPPORT1580inet_ntop(AF_INET6, &addr.sin6_addr, straddr, sizeof(straddr));1581#else1582inet_ntop(AF_INET, &addr.sin_addr, straddr, sizeof(straddr));1583#endif15841585r = server_socket->func(server_socket->data, client_socket,1586straddr);1587#else1588r = server_socket->func(server_socket->data, client_socket,1589inet_ntoa(addr.sin_addr));1590#endif15911592if(r != 0)1593{1594fluid_socket_close(client_socket);1595}1596}1597}15981599FLUID_LOG(FLUID_DBG, "Server closing");16001601return FLUID_THREAD_RETURN_VALUE;1602}16031604fluid_server_socket_t *1605new_fluid_server_socket(int port, fluid_server_func_t func, void *data)1606{1607fluid_server_socket_t *server_socket;1608struct sockaddr_in addr4;1609#ifdef IPV6_SUPPORT1610struct sockaddr_in6 addr6;1611#endif1612const struct sockaddr *addr;1613size_t addr_size;1614fluid_socket_t sock;16151616fluid_return_val_if_fail(func != NULL, NULL);16171618if(fluid_socket_init() != FLUID_OK)1619{1620return NULL;1621}16221623FLUID_MEMSET(&addr4, 0, sizeof(addr4));1624addr4.sin_family = AF_INET;1625addr4.sin_port = htons((uint16_t)port);1626addr4.sin_addr.s_addr = htonl(INADDR_ANY);16271628#ifdef IPV6_SUPPORT1629FLUID_MEMSET(&addr6, 0, sizeof(addr6));1630addr6.sin6_family = AF_INET6;1631addr6.sin6_port = htons((uint16_t)port);1632addr6.sin6_addr = in6addr_any;1633#endif16341635#ifdef IPV6_SUPPORT1636sock = socket(AF_INET6, SOCK_STREAM, 0);1637addr = (const struct sockaddr *) &addr6;1638addr_size = sizeof(addr6);16391640if(sock == INVALID_SOCKET)1641{1642FLUID_LOG(FLUID_WARN, "Failed to create IPv6 server socket: %d (will try with IPv4)", fluid_socket_get_error());16431644sock = socket(AF_INET, SOCK_STREAM, 0);1645addr = (const struct sockaddr *) &addr4;1646addr_size = sizeof(addr4);1647}16481649#else1650sock = socket(AF_INET, SOCK_STREAM, 0);1651addr = (const struct sockaddr *) &addr4;1652addr_size = sizeof(addr4);1653#endif16541655if(sock == INVALID_SOCKET)1656{1657FLUID_LOG(FLUID_ERR, "Failed to create server socket: %d", fluid_socket_get_error());1658fluid_socket_cleanup();1659return NULL;1660}16611662if(bind(sock, addr, addr_size) == SOCKET_ERROR)1663{1664FLUID_LOG(FLUID_ERR, "Failed to bind server socket: %d", fluid_socket_get_error());1665fluid_socket_close(sock);1666fluid_socket_cleanup();1667return NULL;1668}16691670if(listen(sock, SOMAXCONN) == SOCKET_ERROR)1671{1672FLUID_LOG(FLUID_ERR, "Failed to listen on server socket: %d", fluid_socket_get_error());1673fluid_socket_close(sock);1674fluid_socket_cleanup();1675return NULL;1676}16771678server_socket = FLUID_NEW(fluid_server_socket_t);16791680if(server_socket == NULL)1681{1682FLUID_LOG(FLUID_ERR, "Out of memory");1683fluid_socket_close(sock);1684fluid_socket_cleanup();1685return NULL;1686}16871688server_socket->socket = sock;1689server_socket->func = func;1690server_socket->data = data;1691server_socket->cont = 1;16921693server_socket->thread = new_fluid_thread("server", fluid_server_socket_run, server_socket,16940, FALSE);16951696if(server_socket->thread == NULL)1697{1698FLUID_FREE(server_socket);1699fluid_socket_close(sock);1700fluid_socket_cleanup();1701return NULL;1702}17031704return server_socket;1705}17061707void delete_fluid_server_socket(fluid_server_socket_t *server_socket)1708{1709fluid_return_if_fail(server_socket != NULL);17101711server_socket->cont = 0;17121713if(server_socket->socket != INVALID_SOCKET)1714{1715fluid_socket_close(server_socket->socket);1716}17171718if(server_socket->thread)1719{1720fluid_thread_join(server_socket->thread);1721delete_fluid_thread(server_socket->thread);1722}17231724FLUID_FREE(server_socket);17251726// Should be called the same number of times as fluid_socket_init()1727fluid_socket_cleanup();1728}17291730#endif // NETWORK_SUPPORT17311732FILE* fluid_file_open(const char* path, const char** errMsg)1733{1734static const char ErrExist[] = "File does not exist.";1735static const char ErrRegular[] = "File is not regular, refusing to open it.";1736static const char ErrNull[] = "File does not exists or insufficient permissions to open it.";17371738FILE* handle = NULL;17391740if(!fluid_file_test(path, FLUID_FILE_TEST_EXISTS))1741{1742if(errMsg != NULL)1743{1744*errMsg = ErrExist;1745}1746}1747else if(!fluid_file_test(path, FLUID_FILE_TEST_IS_REGULAR))1748{1749if(errMsg != NULL)1750{1751*errMsg = ErrRegular;1752}1753}1754else if((handle = FLUID_FOPEN(path, "rb")) == NULL)1755{1756if(errMsg != NULL)1757{1758*errMsg = ErrNull;1759}1760}17611762return handle;1763}17641765fluid_long_long_t fluid_file_tell(FILE* f)1766{1767#ifdef _WIN321768// On Windows, long is only a 32 bit integer. Thus ftell() does not support to handle files >2GiB.1769// We should use _ftelli64() in this case, however its availability depends on MS CRT and might not be1770// available on WindowsXP, Win98, etc.1771//1772// The web recommends to fallback to _telli64() in this case. However, it's return value differs from1773// _ftelli64() on Win10: https://github.com/FluidSynth/fluidsynth/pull/629#issuecomment-6022384361774//1775// Thus, we use fgetpos().1776fpos_t pos;1777if(fgetpos(f, &pos) != 0)1778{1779return (fluid_long_long_t)-1L;1780}1781return pos;1782#else1783return ftell(f);1784#endif1785}17861787#if defined(_WIN32) || defined(__CYGWIN__)1788// not thread-safe!1789char* fluid_get_windows_error(void)1790{1791static TCHAR err[1024];17921793FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,1794NULL,1795GetLastError(),1796MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),1797err,1798sizeof(err)/sizeof(err[0]),1799NULL);18001801#ifdef _UNICODE1802static char ascii_err[sizeof(err)];18031804WideCharToMultiByte(CP_UTF8, 0, err, -1, ascii_err, sizeof(ascii_err)/sizeof(ascii_err[0]), 0, 0);1805return ascii_err;1806#else1807return err;1808#endif1809}1810#endif181118121813