mirror of
https://github.com/DrBeef/QuestZDoom.git
synced 2025-03-05 17:10:55 +00:00
1575 lines
34 KiB
C
1575 lines
34 KiB
C
/* FluidSynth - A Software Synthesizer
|
|
*
|
|
* Copyright (C) 2003 Peter Hanappe and others.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public License
|
|
* as published by the Free Software Foundation; either version 2 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
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the Free
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
* 02111-1307, USA
|
|
*/
|
|
|
|
|
|
#include "fluid_sys.h"
|
|
|
|
static char fluid_errbuf[512]; /* buffer for error message */
|
|
|
|
static fluid_log_function_t fluid_log_function[LAST_LOG_LEVEL];
|
|
static void* fluid_log_user_data[LAST_LOG_LEVEL];
|
|
static int fluid_log_initialized = 0;
|
|
|
|
static char* fluid_libname = "fluidsynth";
|
|
|
|
|
|
void fluid_sys_config()
|
|
{
|
|
fluid_log_config();
|
|
fluid_time_config();
|
|
}
|
|
|
|
|
|
unsigned int fluid_debug_flags = 0;
|
|
|
|
#if DEBUG
|
|
/*
|
|
* fluid_debug
|
|
*/
|
|
int fluid_debug(int level, char * fmt, ...)
|
|
{
|
|
if (fluid_debug_flags & level) {
|
|
fluid_log_function_t fun;
|
|
va_list args;
|
|
|
|
va_start (args, fmt);
|
|
vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
|
|
va_end (args);
|
|
|
|
fun = fluid_log_function[FLUID_DBG];
|
|
if (fun != NULL) {
|
|
(*fun)(level, fluid_errbuf, fluid_log_user_data[FLUID_DBG]);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Installs a new log function for a specified log level.
|
|
* @param level Log level to install handler for.
|
|
* @param fun Callback function handler to call for logged messages
|
|
* @param data User supplied data pointer to pass to log function
|
|
* @return The previously installed function.
|
|
*/
|
|
fluid_log_function_t
|
|
fluid_set_log_function(int level, fluid_log_function_t fun, void* data)
|
|
{
|
|
fluid_log_function_t old = NULL;
|
|
|
|
if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
|
|
old = fluid_log_function[level];
|
|
fluid_log_function[level] = fun;
|
|
fluid_log_user_data[level] = data;
|
|
}
|
|
return old;
|
|
}
|
|
|
|
/**
|
|
* Default log function which prints to the stderr.
|
|
* @param level Log level
|
|
* @param message Log message
|
|
* @param data User supplied data (not used)
|
|
*/
|
|
void
|
|
fluid_default_log_function(int level, char* message, void* data)
|
|
{
|
|
FILE* out;
|
|
|
|
#if defined(WIN32)
|
|
out = stdout;
|
|
#else
|
|
out = stderr;
|
|
#endif
|
|
|
|
if (fluid_log_initialized == 0) {
|
|
fluid_log_config();
|
|
}
|
|
|
|
switch (level) {
|
|
case FLUID_PANIC:
|
|
FLUID_FPRINTF(out, "%s: panic: %s\n", fluid_libname, message);
|
|
break;
|
|
case FLUID_ERR:
|
|
FLUID_FPRINTF(out, "%s: error: %s\n", fluid_libname, message);
|
|
break;
|
|
case FLUID_WARN:
|
|
FLUID_FPRINTF(out, "%s: warning: %s\n", fluid_libname, message);
|
|
break;
|
|
case FLUID_INFO:
|
|
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
|
|
break;
|
|
case FLUID_DBG:
|
|
#if DEBUG
|
|
FLUID_FPRINTF(out, "%s: debug: %s\n", fluid_libname, message);
|
|
#endif
|
|
break;
|
|
default:
|
|
FLUID_FPRINTF(out, "%s: %s\n", fluid_libname, message);
|
|
break;
|
|
}
|
|
fflush(out);
|
|
}
|
|
|
|
/*
|
|
* fluid_init_log
|
|
*/
|
|
void
|
|
fluid_log_config(void)
|
|
{
|
|
if (fluid_log_initialized == 0) {
|
|
|
|
fluid_log_initialized = 1;
|
|
|
|
if (fluid_log_function[FLUID_PANIC] == NULL) {
|
|
fluid_set_log_function(FLUID_PANIC, fluid_default_log_function, NULL);
|
|
}
|
|
|
|
if (fluid_log_function[FLUID_ERR] == NULL) {
|
|
fluid_set_log_function(FLUID_ERR, fluid_default_log_function, NULL);
|
|
}
|
|
|
|
if (fluid_log_function[FLUID_WARN] == NULL) {
|
|
fluid_set_log_function(FLUID_WARN, fluid_default_log_function, NULL);
|
|
}
|
|
|
|
if (fluid_log_function[FLUID_INFO] == NULL) {
|
|
fluid_set_log_function(FLUID_INFO, fluid_default_log_function, NULL);
|
|
}
|
|
|
|
if (fluid_log_function[FLUID_DBG] == NULL) {
|
|
fluid_set_log_function(FLUID_DBG, fluid_default_log_function, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print a message to the log.
|
|
* @param level Log level (#fluid_log_level).
|
|
* @param fmt Printf style format string for log message
|
|
* @param ... Arguments for printf 'fmt' message string
|
|
* @return Always returns -1
|
|
*/
|
|
int
|
|
fluid_log(int level, char* fmt, ...)
|
|
{
|
|
fluid_log_function_t fun = NULL;
|
|
|
|
va_list args;
|
|
va_start (args, fmt);
|
|
vsnprintf(fluid_errbuf, sizeof (fluid_errbuf), fmt, args);
|
|
va_end (args);
|
|
|
|
if ((level >= 0) && (level < LAST_LOG_LEVEL)) {
|
|
fun = fluid_log_function[level];
|
|
if (fun != NULL) {
|
|
(*fun)(level, fluid_errbuf, fluid_log_user_data[level]);
|
|
}
|
|
}
|
|
return FLUID_FAILED;
|
|
}
|
|
|
|
/**
|
|
* An improved strtok, still trashes the input string, but is portable and
|
|
* thread safe. Also skips token chars at beginning of token string and never
|
|
* returns an empty token (will return NULL if source ends in token chars though).
|
|
* NOTE: NOT part of public API
|
|
* @internal
|
|
* @param str Pointer to a string pointer of source to tokenize. Pointer gets
|
|
* updated on each invocation to point to beginning of next token. Note that
|
|
* token char get's overwritten with a 0 byte. String pointer is set to NULL
|
|
* when final token is returned.
|
|
* @param delim String of delimiter chars.
|
|
* @return Pointer to the next token or NULL if no more tokens.
|
|
*/
|
|
char *fluid_strtok (char **str, char *delim)
|
|
{
|
|
char *s, *d, *token;
|
|
char c;
|
|
|
|
if (str == NULL || delim == NULL || !*delim)
|
|
{
|
|
FLUID_LOG(FLUID_ERR, "Null pointer");
|
|
return NULL;
|
|
}
|
|
|
|
s = *str;
|
|
if (!s) return NULL; /* str points to a NULL pointer? (tokenize already ended) */
|
|
|
|
/* skip delimiter chars at beginning of token */
|
|
do
|
|
{
|
|
c = *s;
|
|
if (!c) /* end of source string? */
|
|
{
|
|
*str = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
for (d = delim; *d; d++) /* is source char a token char? */
|
|
{
|
|
if (c == *d) /* token char match? */
|
|
{
|
|
s++; /* advance to next source char */
|
|
break;
|
|
}
|
|
}
|
|
} while (*d); /* while token char match */
|
|
|
|
token = s; /* start of token found */
|
|
|
|
/* search for next token char or end of source string */
|
|
for (s = s+1; *s; s++)
|
|
{
|
|
c = *s;
|
|
|
|
for (d = delim; *d; d++) /* is source char a token char? */
|
|
{
|
|
if (c == *d) /* token char match? */
|
|
{
|
|
*s = '\0'; /* overwrite token char with zero byte to terminate token */
|
|
*str = s+1; /* update str to point to beginning of next token */
|
|
return token;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we get here only if source string ended */
|
|
*str = NULL;
|
|
return token;
|
|
}
|
|
|
|
/*
|
|
* fluid_error
|
|
*/
|
|
char*
|
|
fluid_error()
|
|
{
|
|
return fluid_errbuf;
|
|
}
|
|
|
|
|
|
/*
|
|
*
|
|
* fluid_is_midifile
|
|
*/
|
|
int
|
|
fluid_is_midifile(char* filename)
|
|
{
|
|
FILE* fp = fopen(filename, "rb");
|
|
char id[4];
|
|
|
|
if (fp == NULL) {
|
|
return 0;
|
|
}
|
|
if (fread((void*) id, 1, 4, fp) != 4) {
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
fclose(fp);
|
|
|
|
return strncmp(id, "MThd", 4) == 0;
|
|
}
|
|
|
|
/*
|
|
* fluid_is_soundfont
|
|
*
|
|
*/
|
|
int
|
|
fluid_is_soundfont(char* filename)
|
|
{
|
|
FILE* fp = fopen(filename, "rb");
|
|
char id[4];
|
|
|
|
if (fp == NULL) {
|
|
return 0;
|
|
}
|
|
if (fread((void*) id, 1, 4, fp) != 4) {
|
|
fclose(fp);
|
|
return 0;
|
|
}
|
|
fclose(fp);
|
|
|
|
return strncmp(id, "RIFF", 4) == 0;
|
|
}
|
|
|
|
#if defined(WIN32)
|
|
|
|
/*=============================================================*/
|
|
/* */
|
|
/* Win32 */
|
|
/* */
|
|
/*=============================================================*/
|
|
|
|
/***************************************************************
|
|
*
|
|
* Timer
|
|
*
|
|
*/
|
|
|
|
struct _fluid_timer_t
|
|
{
|
|
long msec;
|
|
fluid_timer_callback_t callback;
|
|
void* data;
|
|
HANDLE thread;
|
|
DWORD thread_id;
|
|
int cont;
|
|
int auto_destroy;
|
|
};
|
|
|
|
static int fluid_timer_count = 0;
|
|
DWORD WINAPI fluid_timer_run(LPVOID data);
|
|
|
|
fluid_timer_t*
|
|
new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data,
|
|
int new_thread, int auto_destroy)
|
|
{
|
|
fluid_timer_t* timer = FLUID_NEW(fluid_timer_t);
|
|
if (timer == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
timer->cont = 1;
|
|
timer->msec = msec;
|
|
timer->callback = callback;
|
|
timer->data = data;
|
|
timer->thread = 0;
|
|
timer->auto_destroy = auto_destroy;
|
|
|
|
if (new_thread) {
|
|
timer->thread = CreateThread(NULL, 0, fluid_timer_run, (LPVOID) timer, 0, &timer->thread_id);
|
|
if (timer->thread == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Couldn't create timer thread");
|
|
FLUID_FREE(timer);
|
|
return NULL;
|
|
}
|
|
SetThreadPriority(timer->thread, THREAD_PRIORITY_TIME_CRITICAL);
|
|
} else {
|
|
fluid_timer_run((LPVOID) timer);
|
|
}
|
|
return timer;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
fluid_timer_run(LPVOID data)
|
|
{
|
|
int count = 0;
|
|
int cont = 1;
|
|
long start;
|
|
long delay;
|
|
fluid_timer_t* timer;
|
|
timer = (fluid_timer_t*) data;
|
|
|
|
if ((timer == NULL) || (timer->callback == NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|
|
|
/* keep track of the start time for absolute positioning */
|
|
start = fluid_curtime();
|
|
|
|
while (cont) {
|
|
|
|
/* do whatever we have to do */
|
|
cont = (*timer->callback)(timer->data, fluid_curtime() - start);
|
|
|
|
count++;
|
|
|
|
/* to avoid incremental time errors, I calculate the delay between
|
|
two callbacks bringing in the "absolute" time (count *
|
|
timer->msec) */
|
|
delay = (count * timer->msec) - (fluid_curtime() - start);
|
|
if (delay > 0) {
|
|
Sleep(delay);
|
|
}
|
|
|
|
cont &= timer->cont;
|
|
}
|
|
|
|
FLUID_LOG(FLUID_DBG, "Timer thread finished");
|
|
|
|
if (timer->auto_destroy) {
|
|
FLUID_FREE(timer);
|
|
}
|
|
|
|
ExitThread(0);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
delete_fluid_timer(fluid_timer_t* timer)
|
|
{
|
|
timer->cont = 0;
|
|
fluid_timer_join(timer);
|
|
FLUID_FREE(timer);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int
|
|
fluid_timer_join(fluid_timer_t* timer)
|
|
{
|
|
DWORD wait_result;
|
|
if (timer->thread == 0) {
|
|
return FLUID_OK;
|
|
}
|
|
wait_result = WaitForSingleObject(timer->thread, INFINITE);
|
|
return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Time
|
|
*/
|
|
|
|
double rdtsc(void);
|
|
double fluid_estimate_cpu_frequency(void);
|
|
|
|
static double fluid_cpu_frequency = -1.0;
|
|
|
|
void fluid_time_config(void)
|
|
{
|
|
if (fluid_cpu_frequency < 0.0) {
|
|
fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0;
|
|
}
|
|
}
|
|
|
|
double fluid_utime(void)
|
|
{
|
|
return (rdtsc() / fluid_cpu_frequency);
|
|
}
|
|
|
|
double rdtsc(void)
|
|
{
|
|
LARGE_INTEGER t;
|
|
QueryPerformanceCounter(&t);
|
|
return (double) t.QuadPart;
|
|
}
|
|
|
|
double fluid_estimate_cpu_frequency(void)
|
|
{
|
|
#if 0
|
|
LONGLONG start, stop, ticks;
|
|
unsigned int before, after, delta;
|
|
double freq;
|
|
|
|
start = rdtsc();
|
|
stop = start;
|
|
before = fluid_curtime();
|
|
after = before;
|
|
|
|
while (1) {
|
|
if (after - before > 1000) {
|
|
break;
|
|
}
|
|
after = fluid_curtime();
|
|
stop = rdtsc();
|
|
}
|
|
|
|
delta = after - before;
|
|
ticks = stop - start;
|
|
|
|
freq = 1000 * ticks / delta;
|
|
|
|
return freq;
|
|
|
|
#else
|
|
unsigned int before, after;
|
|
LARGE_INTEGER start, stop;
|
|
|
|
before = fluid_curtime();
|
|
QueryPerformanceCounter(&start);
|
|
|
|
Sleep(1000);
|
|
|
|
after = fluid_curtime();
|
|
QueryPerformanceCounter(&stop);
|
|
|
|
return (double) 1000 * (stop.QuadPart - start.QuadPart) / (after - before);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#elif defined(MACOS9)
|
|
/*=============================================================*/
|
|
/* */
|
|
/* MacOS 9 */
|
|
/* */
|
|
/*=============================================================*/
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Timer
|
|
*/
|
|
|
|
struct _fluid_timer_t
|
|
{
|
|
TMTask myTmTask;
|
|
long msec;
|
|
unsigned int start;
|
|
unsigned int count;
|
|
int isInstalled;
|
|
fluid_timer_callback_t callback;
|
|
void* data;
|
|
int auto_destroy;
|
|
};
|
|
|
|
static TimerUPP myTimerUPP;
|
|
|
|
void
|
|
_timerCallback(fluid_timer_t *timer)
|
|
{
|
|
int cont;
|
|
cont = (*timer->callback)(timer->data, fluid_curtime() - timer->start);
|
|
if (cont) {
|
|
PrimeTime((QElemPtr)timer, timer->msec);
|
|
} else {
|
|
timer->isInstalled = 0;
|
|
}
|
|
timer->count++;
|
|
}
|
|
|
|
fluid_timer_t*
|
|
new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data,
|
|
int new_thread, int auto_destroy)
|
|
{
|
|
fluid_timer_t* timer = FLUID_NEW(fluid_timer_t);
|
|
if (timer == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
if (!myTimerUPP)
|
|
myTimerUPP = NewTimerProc(_timerCallback);
|
|
|
|
/* setup tmtask */
|
|
timer->myTmTask.tmAddr = myTimerUPP;
|
|
timer->myTmTask.qLink = NULL;
|
|
timer->myTmTask.qType = 0;
|
|
timer->myTmTask.tmCount = 0L;
|
|
timer->myTmTask.tmWakeUp = 0L;
|
|
timer->myTmTask.tmReserved = 0L;
|
|
|
|
timer->callback = callback;
|
|
|
|
timer->msec = msec;
|
|
timer->data = data;
|
|
timer->start = fluid_curtime();
|
|
timer->isInstalled = 1;
|
|
timer->count = 0;
|
|
timer->auto_destroy = auto_destroy;
|
|
|
|
InsXTime((QElemPtr)timer);
|
|
PrimeTime((QElemPtr)timer, msec);
|
|
|
|
return timer;
|
|
}
|
|
|
|
int
|
|
delete_fluid_timer(fluid_timer_t* timer)
|
|
{
|
|
if (timer->isInstalled) {
|
|
RmvTime((QElemPtr)timer);
|
|
}
|
|
FLUID_FREE(timer);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int
|
|
fluid_timer_join(fluid_timer_t* timer)
|
|
{
|
|
if (timer->isInstalled) {
|
|
int count = timer->count;
|
|
/* wait until count has incremented */
|
|
while (count == timer->count) {}
|
|
}
|
|
return FLUID_OK;
|
|
}
|
|
|
|
/***************************************************************
|
|
*
|
|
* Time
|
|
*/
|
|
#define kTwoPower32 (4294967296.0) /* 2^32 */
|
|
|
|
void fluid_time_config(void)
|
|
{
|
|
}
|
|
|
|
unsigned int fluid_curtime()
|
|
{
|
|
/* could be optimized by not going though a double */
|
|
UnsignedWide uS;
|
|
double mSf;
|
|
unsigned int ms;
|
|
|
|
Microseconds(&uS);
|
|
|
|
mSf = ((((double) uS.hi) * kTwoPower32) + uS.lo)/1000.0f;
|
|
|
|
ms = mSf;
|
|
|
|
return (ms);
|
|
}
|
|
|
|
|
|
|
|
#elif defined(__OS2__)
|
|
/*=============================================================*/
|
|
/* */
|
|
/* OS2 */
|
|
/* */
|
|
/*=============================================================*/
|
|
|
|
/***************************************************************
|
|
*
|
|
* Timer
|
|
*
|
|
*/
|
|
|
|
struct _fluid_timer_t
|
|
{
|
|
long msec;
|
|
fluid_timer_callback_t callback;
|
|
void* data;
|
|
int thread_id;
|
|
int cont;
|
|
int auto_destroy;
|
|
};
|
|
|
|
static int fluid_timer_count = 0;
|
|
void fluid_timer_run(void *data);
|
|
|
|
fluid_timer_t*
|
|
new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data,
|
|
int new_thread, int auto_destroy)
|
|
{
|
|
fluid_timer_t* timer = FLUID_NEW(fluid_timer_t);
|
|
if (timer == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
timer->cont = 1;
|
|
timer->msec = msec;
|
|
timer->callback = callback;
|
|
timer->data = data;
|
|
timer->thread_id =-1;
|
|
timer->auto_destroy = auto_destroy;
|
|
|
|
if (new_thread) {
|
|
timer->thread_id = _beginthread( fluid_timer_run, NULL, 256 * 1024, ( void * )timer );
|
|
if (timer->thread_id == -1) {
|
|
FLUID_LOG(FLUID_ERR, "Couldn't create timer thread");
|
|
FLUID_FREE(timer);
|
|
return NULL;
|
|
}
|
|
DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, PRTYD_MAXIMUM, timer->thread_id);
|
|
} else {
|
|
fluid_timer_run(( void * )timer);
|
|
}
|
|
return timer;
|
|
}
|
|
|
|
void
|
|
fluid_timer_run(void *data)
|
|
{
|
|
int count = 0;
|
|
int cont = 1;
|
|
long start;
|
|
long delay;
|
|
fluid_timer_t* timer;
|
|
timer = (fluid_timer_t*) data;
|
|
|
|
if ((timer == NULL) || (timer->callback == NULL)) {
|
|
return;
|
|
}
|
|
|
|
DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, 0 );
|
|
|
|
/* keep track of the start time for absolute positioning */
|
|
start = fluid_curtime();
|
|
|
|
while (cont) {
|
|
|
|
/* do whatever we have to do */
|
|
cont = (*timer->callback)(timer->data, fluid_curtime() - start);
|
|
|
|
count++;
|
|
|
|
/* to avoid incremental time errors, I calculate the delay between
|
|
two callbacks bringing in the "absolute" time (count *
|
|
timer->msec) */
|
|
delay = (count * timer->msec) - (fluid_curtime() - start);
|
|
if (delay > 0) {
|
|
DosSleep(delay);
|
|
}
|
|
|
|
cont &= timer->cont;
|
|
}
|
|
|
|
FLUID_LOG(FLUID_DBG, "Timer thread finished");
|
|
|
|
if (timer->auto_destroy) {
|
|
FLUID_FREE(timer);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
delete_fluid_timer(fluid_timer_t* timer)
|
|
{
|
|
timer->cont = 0;
|
|
fluid_timer_join(timer);
|
|
FLUID_FREE(timer);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int
|
|
fluid_timer_join(fluid_timer_t* timer)
|
|
{
|
|
ULONG wait_result;
|
|
if (timer->thread_id == -1) {
|
|
return FLUID_OK;
|
|
}
|
|
wait_result = DosWaitThread(&timer->thread_id, DCWW_WAIT);
|
|
return (wait_result == 0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Time
|
|
*/
|
|
|
|
double rdtsc(void);
|
|
double fluid_estimate_cpu_frequency(void);
|
|
|
|
static double fluid_cpu_frequency = -1.0;
|
|
|
|
void fluid_time_config(void)
|
|
{
|
|
if (fluid_cpu_frequency < 0.0) {
|
|
fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0;
|
|
}
|
|
}
|
|
|
|
unsigned int fluid_curtime(void)
|
|
{
|
|
ULONG ulMS;
|
|
DosQuerySysInfo( QSV_MS_COUNT, QSV_MS_COUNT, &ulMS, sizeof( ULONG ));
|
|
return ulMS;
|
|
}
|
|
|
|
double fluid_utime(void)
|
|
{
|
|
return (rdtsc() / fluid_cpu_frequency);
|
|
}
|
|
|
|
#define Q2ULL( q ) (*(unsigned long long *)&q)
|
|
|
|
double rdtsc(void)
|
|
{
|
|
QWORD t;
|
|
DosTmrQueryTime(&t);
|
|
return (double)Q2ULL(t);
|
|
}
|
|
|
|
double fluid_estimate_cpu_frequency(void)
|
|
{
|
|
unsigned int before, after;
|
|
QWORD start, stop;
|
|
|
|
before = fluid_curtime();
|
|
DosTmrQueryTime(&start);
|
|
|
|
DosSleep(1000);
|
|
|
|
after = fluid_curtime();
|
|
DosTmrQueryTime(&stop);
|
|
|
|
return (double) 1000 * (Q2ULL(stop) - Q2ULL(start)) / (after - before);
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
/*=============================================================*/
|
|
/* */
|
|
/* POSIX */
|
|
/* */
|
|
/*=============================================================*/
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Timer
|
|
*/
|
|
|
|
struct _fluid_timer_t
|
|
{
|
|
long msec;
|
|
fluid_timer_callback_t callback;
|
|
void* data;
|
|
pthread_t thread;
|
|
int cont;
|
|
int auto_destroy;
|
|
};
|
|
|
|
void*
|
|
fluid_timer_start(void *data)
|
|
{
|
|
int count = 0;
|
|
int cont = 1;
|
|
long start;
|
|
long delay;
|
|
fluid_timer_t* timer;
|
|
timer = (fluid_timer_t*) data;
|
|
|
|
/* keep track of the start time for absolute positioning */
|
|
start = fluid_curtime();
|
|
|
|
while (cont) {
|
|
|
|
/* do whatever we have to do */
|
|
cont = (*timer->callback)(timer->data, fluid_curtime() - start);
|
|
|
|
count++;
|
|
|
|
/* to avoid incremental time errors, calculate the delay between
|
|
two callbacks bringing in the "absolute" time (count *
|
|
timer->msec) */
|
|
delay = (count * timer->msec) - (fluid_curtime() - start);
|
|
if (delay > 0) {
|
|
usleep(delay * 1000);
|
|
}
|
|
|
|
cont &= timer->cont;
|
|
}
|
|
|
|
FLUID_LOG(FLUID_DBG, "Timer thread finished");
|
|
if (timer->thread != 0) {
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
if (timer->auto_destroy) {
|
|
FLUID_FREE(timer);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
fluid_timer_t*
|
|
new_fluid_timer(int msec, fluid_timer_callback_t callback, void* data,
|
|
int new_thread, int auto_destroy)
|
|
{
|
|
pthread_attr_t *attr = NULL;
|
|
pthread_attr_t rt_attr;
|
|
int sched = SCHED_FIFO;
|
|
struct sched_param priority;
|
|
int err;
|
|
|
|
fluid_timer_t* timer = FLUID_NEW(fluid_timer_t);
|
|
if (timer == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
timer->msec = msec;
|
|
timer->callback = callback;
|
|
timer->data = data;
|
|
timer->cont = 1;
|
|
timer->thread = 0;
|
|
timer->auto_destroy = auto_destroy;
|
|
|
|
err = pthread_attr_init(&rt_attr);
|
|
if (err == 0) {
|
|
err = pthread_attr_setschedpolicy(&rt_attr, SCHED_FIFO);
|
|
if (err == 0) {
|
|
priority.sched_priority = 10;
|
|
err = pthread_attr_setschedparam(&rt_attr, &priority);
|
|
if (err == 0) {
|
|
attr = &rt_attr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (new_thread) {
|
|
err = pthread_create(&timer->thread, attr, fluid_timer_start, (void*) timer);
|
|
if (err == 0) {
|
|
FLUID_LOG(FLUID_DBG, "The timer thread was created with real-time priority");
|
|
} else {
|
|
/* Create the thread with default attributes */
|
|
err = pthread_create(&timer->thread, NULL, fluid_timer_start, (void*) timer);
|
|
if (err != 0) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to create the timer thread");
|
|
FLUID_FREE(timer);
|
|
return NULL;
|
|
} else {
|
|
FLUID_LOG(FLUID_DBG, "The timer thread does not have real-time priority");
|
|
}
|
|
}
|
|
} else {
|
|
fluid_timer_start((void*) timer);
|
|
}
|
|
return timer;
|
|
}
|
|
|
|
int
|
|
delete_fluid_timer(fluid_timer_t* timer)
|
|
{
|
|
timer->cont = 0;
|
|
fluid_timer_join(timer);
|
|
FLUID_LOG(FLUID_DBG, "Joined player thread");
|
|
FLUID_FREE(timer);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int
|
|
fluid_timer_join(fluid_timer_t* timer)
|
|
{
|
|
int err = 0;
|
|
|
|
if (timer->thread != 0) {
|
|
err = pthread_join(timer->thread, NULL);
|
|
}
|
|
FLUID_LOG(FLUID_DBG, "Joined player thread");
|
|
return (err == 0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Time
|
|
*/
|
|
|
|
static double fluid_cpu_frequency = -1.0;
|
|
|
|
double rdtsc(void);
|
|
double fluid_estimate_cpu_frequency(void);
|
|
|
|
void fluid_time_config(void)
|
|
{
|
|
if (fluid_cpu_frequency < 0.0) {
|
|
fluid_cpu_frequency = fluid_estimate_cpu_frequency() / 1000000.0;
|
|
if (fluid_cpu_frequency == 0.0) fluid_cpu_frequency = 1.0;
|
|
}
|
|
}
|
|
|
|
unsigned int fluid_curtime()
|
|
{
|
|
struct timeval now;
|
|
gettimeofday(&now, NULL);
|
|
return now.tv_sec * 1000 + now.tv_usec / 1000;
|
|
}
|
|
|
|
double fluid_utime(void)
|
|
{
|
|
return (rdtsc() / fluid_cpu_frequency);
|
|
}
|
|
|
|
#if !defined(__i386__)
|
|
|
|
double rdtsc(void)
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
double fluid_estimate_cpu_frequency(void)
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
#else
|
|
|
|
double rdtsc(void)
|
|
{
|
|
unsigned int a, b;
|
|
|
|
__asm__ ("rdtsc" : "=a" (a), "=d" (b));
|
|
return (double)b * (double)0x10000 * (double)0x10000 + a;
|
|
}
|
|
|
|
double fluid_estimate_cpu_frequency(void)
|
|
{
|
|
double start, stop;
|
|
unsigned int a0, b0, a1, b1;
|
|
unsigned int before, after;
|
|
|
|
before = fluid_curtime();
|
|
__asm__ ("rdtsc" : "=a" (a0), "=d" (b0));
|
|
|
|
sleep(1);
|
|
|
|
after = fluid_curtime();
|
|
__asm__ ("rdtsc" : "=a" (a1), "=d" (b1));
|
|
|
|
|
|
start = (double)b0 * (double)0x10000 * (double)0x10000 + a0;
|
|
stop = (double)b1 * (double)0x10000 * (double)0x10000 + a1;
|
|
|
|
return 1000 * (stop - start) / (after - before);
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef FPE_CHECK
|
|
|
|
/***************************************************************
|
|
*
|
|
* Floating point exceptions
|
|
*
|
|
* The floating point exception functions were taken from Ircam's
|
|
* jMax source code. http://www.ircam.fr/jmax
|
|
*
|
|
* FIXME: check in config for i386 machine
|
|
*
|
|
* Currently not used. I leave the code here in case we want to pick
|
|
* this up again some time later.
|
|
*/
|
|
|
|
/* Exception flags */
|
|
#define _FPU_STATUS_IE 0x001 /* Invalid Operation */
|
|
#define _FPU_STATUS_DE 0x002 /* Denormalized Operand */
|
|
#define _FPU_STATUS_ZE 0x004 /* Zero Divide */
|
|
#define _FPU_STATUS_OE 0x008 /* Overflow */
|
|
#define _FPU_STATUS_UE 0x010 /* Underflow */
|
|
#define _FPU_STATUS_PE 0x020 /* Precision */
|
|
#define _FPU_STATUS_SF 0x040 /* Stack Fault */
|
|
#define _FPU_STATUS_ES 0x080 /* Error Summary Status */
|
|
|
|
/* Macros for accessing the FPU status word. */
|
|
|
|
/* get the FPU status */
|
|
#define _FPU_GET_SW(sw) __asm__ ("fnstsw %0" : "=m" (*&sw))
|
|
|
|
/* clear the FPU status */
|
|
#define _FPU_CLR_SW() __asm__ ("fnclex" : : )
|
|
|
|
/* Purpose:
|
|
* Checks, if the floating point unit has produced an exception, print a message
|
|
* if so and clear the exception.
|
|
*/
|
|
unsigned int fluid_check_fpe_i386(char* explanation)
|
|
{
|
|
unsigned int s;
|
|
|
|
_FPU_GET_SW(s);
|
|
_FPU_CLR_SW();
|
|
|
|
s &= _FPU_STATUS_IE | _FPU_STATUS_DE | _FPU_STATUS_ZE | _FPU_STATUS_OE | _FPU_STATUS_UE;
|
|
|
|
if (s)
|
|
{
|
|
FLUID_LOG(FLUID_WARN, "FPE exception (before or in %s): %s%s%s%s%s", explanation,
|
|
(s & _FPU_STATUS_IE) ? "Invalid operation " : "",
|
|
(s & _FPU_STATUS_DE) ? "Denormal number " : "",
|
|
(s & _FPU_STATUS_ZE) ? "Zero divide " : "",
|
|
(s & _FPU_STATUS_OE) ? "Overflow " : "",
|
|
(s & _FPU_STATUS_UE) ? "Underflow " : "");
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/* Purpose:
|
|
* Clear floating point exception.
|
|
*/
|
|
void fluid_clear_fpe_i386 (void)
|
|
{
|
|
_FPU_CLR_SW();
|
|
}
|
|
|
|
#endif // ifdef FPE_CHECK
|
|
|
|
|
|
#endif // #else (its POSIX)
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Profiling (Linux, i586 only)
|
|
*
|
|
*/
|
|
|
|
#if WITH_PROFILING
|
|
|
|
fluid_profile_data_t fluid_profile_data[] =
|
|
{
|
|
{ FLUID_PROF_WRITE_S16, "fluid_synth_write_s16 ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK, "fluid_synth_one_block ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK_CLEAR, "fluid_synth_one_block:clear ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK_VOICE, "fluid_synth_one_block:one voice ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK_VOICES, "fluid_synth_one_block:all voices", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK_REVERB, "fluid_synth_one_block:reverb ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_ONE_BLOCK_CHORUS, "fluid_synth_one_block:chorus ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_VOICE_NOTE, "fluid_voice:note ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_VOICE_RELEASE, "fluid_voice:release ", 1e10, 0.0, 0.0, 0},
|
|
{ FLUID_PROF_LAST, "last", 1e100, 0.0, 0.0, 0}
|
|
};
|
|
|
|
|
|
void fluid_profiling_print(void)
|
|
{
|
|
int i;
|
|
|
|
printf("fluid_profiling_print\n");
|
|
|
|
FLUID_LOG(FLUID_INFO, "Estimated CPU frequency: %.0f MHz", fluid_cpu_frequency);
|
|
FLUID_LOG(FLUID_INFO, "Estimated times: min/avg/max (micro seconds)");
|
|
|
|
for (i = 0; i < FLUID_PROF_LAST; i++) {
|
|
if (fluid_profile_data[i].count > 0) {
|
|
FLUID_LOG(FLUID_INFO, "%s: %.3f/%.3f/%.3f",
|
|
fluid_profile_data[i].description,
|
|
fluid_profile_data[i].min,
|
|
fluid_profile_data[i].total / fluid_profile_data[i].count,
|
|
fluid_profile_data[i].max);
|
|
} else {
|
|
FLUID_LOG(FLUID_DBG, "%s: no profiling available", fluid_profile_data[i].description);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endif /* WITH_PROFILING */
|
|
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Threads
|
|
*
|
|
*/
|
|
|
|
#if defined(MACOS9)
|
|
/* Not implemented */
|
|
fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach) { return NULL; }
|
|
int delete_fluid_thread(fluid_thread_t* thread) { return 0; }
|
|
int fluid_thread_join(fluid_thread_t* thread) { return 0; }
|
|
|
|
#elif defined(WIN32)
|
|
|
|
struct _fluid_thread_t {
|
|
HANDLE thread;
|
|
DWORD thread_id;
|
|
fluid_thread_func_t func;
|
|
void* data;
|
|
int detached;
|
|
};
|
|
|
|
static DWORD WINAPI fluid_thread_start(LPVOID data)
|
|
{
|
|
fluid_thread_t* thread = (fluid_thread_t*) data;
|
|
|
|
thread->func(thread->data);
|
|
|
|
if (thread->detached) {
|
|
FLUID_FREE(thread);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach)
|
|
{
|
|
fluid_thread_t* thread;
|
|
|
|
if (func == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Invalid thread function");
|
|
return NULL;
|
|
}
|
|
|
|
thread = FLUID_NEW(fluid_thread_t);
|
|
if (thread == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
thread->data = data;
|
|
thread->func = func;
|
|
thread->detached = detach;
|
|
|
|
thread->thread = CreateThread(NULL, 0, fluid_thread_start, (LPVOID) thread,
|
|
0, &thread->thread_id);
|
|
if (thread->thread == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Couldn't create the thread");
|
|
FLUID_FREE(thread);
|
|
return NULL;
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
int delete_fluid_thread(fluid_thread_t* thread)
|
|
{
|
|
FLUID_FREE(thread);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
|
|
int fluid_thread_join(fluid_thread_t* thread)
|
|
{
|
|
DWORD wait_result;
|
|
if (thread->thread == 0) {
|
|
return FLUID_OK;
|
|
}
|
|
wait_result = WaitForSingleObject(thread->thread, INFINITE);
|
|
return (wait_result == WAIT_OBJECT_0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
#elif defined(__OS2__)
|
|
|
|
struct _fluid_thread_t {
|
|
int thread_id;
|
|
fluid_thread_func_t func;
|
|
void* data;
|
|
int detached;
|
|
};
|
|
|
|
static void fluid_thread_start(void *data)
|
|
{
|
|
fluid_thread_t* thread = (fluid_thread_t*) data;
|
|
|
|
thread->func(thread->data);
|
|
|
|
if (thread->detached) {
|
|
FLUID_FREE(thread);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach)
|
|
{
|
|
fluid_thread_t* thread;
|
|
|
|
if (func == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Invalid thread function");
|
|
return NULL;
|
|
}
|
|
|
|
thread = FLUID_NEW(fluid_thread_t);
|
|
if (thread == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
thread->data = data;
|
|
thread->func = func;
|
|
thread->detached = detach;
|
|
|
|
thread->thread_id = _beginthread(fluid_thread_start, NULL, 256 * 1024, (void *) thread);
|
|
if (thread->thread_id == -1) {
|
|
FLUID_LOG(FLUID_ERR, "Couldn't create the thread");
|
|
FLUID_FREE(thread);
|
|
return NULL;
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
int delete_fluid_thread(fluid_thread_t* thread)
|
|
{
|
|
FLUID_FREE(thread);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
|
|
int fluid_thread_join(fluid_thread_t* thread)
|
|
{
|
|
ULONG wait_result;
|
|
if (thread->thread_id == -1) {
|
|
return FLUID_OK;
|
|
}
|
|
wait_result = DosWaitThread(&thread->thread_id, DCWW_WAIT);
|
|
return (wait_result == 0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
struct _fluid_thread_t {
|
|
pthread_t pthread;
|
|
fluid_thread_func_t func;
|
|
void* data;
|
|
int detached;
|
|
};
|
|
|
|
static void* fluid_thread_start(void *data)
|
|
{
|
|
fluid_thread_t* thread = (fluid_thread_t*) data;
|
|
|
|
thread->func(thread->data);
|
|
|
|
if (thread->detached) {
|
|
FLUID_FREE(thread);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
fluid_thread_t* new_fluid_thread(fluid_thread_func_t func, void* data, int detach)
|
|
{
|
|
fluid_thread_t* thread;
|
|
pthread_attr_t attr;
|
|
|
|
if (func == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Invalid thread function");
|
|
return NULL;
|
|
}
|
|
|
|
thread = FLUID_NEW(fluid_thread_t);
|
|
if (thread == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
return NULL;
|
|
}
|
|
|
|
thread->data = data;
|
|
thread->func = func;
|
|
thread->detached = detach;
|
|
|
|
pthread_attr_init(&attr);
|
|
|
|
if (detach) {
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
}
|
|
|
|
if (pthread_create(&thread->pthread, &attr, fluid_thread_start, thread)) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to create the thread");
|
|
FLUID_FREE(thread);
|
|
return NULL;
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
int delete_fluid_thread(fluid_thread_t* thread)
|
|
{
|
|
FLUID_FREE(thread);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int fluid_thread_join(fluid_thread_t* thread)
|
|
{
|
|
int err = 0;
|
|
|
|
if (thread->pthread != 0) {
|
|
err = pthread_join(thread->pthread, NULL);
|
|
}
|
|
return (err == 0)? FLUID_OK : FLUID_FAILED;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/***************************************************************
|
|
*
|
|
* Sockets
|
|
*
|
|
*/
|
|
|
|
|
|
#if defined(MACINTOSH)
|
|
/* Not implemented */
|
|
|
|
|
|
#elif defined(WIN32)
|
|
|
|
#if 0
|
|
typedef unsigned int socklen_t;
|
|
|
|
#define fluid_socket_read(_S,_B,_L) recv(_S,_B,_L,0)
|
|
#define fluid_socket_write(_S,_B,_L) send(_S,_B,_L,0)
|
|
|
|
void fluid_socket_close(fluid_socket_t sock)
|
|
{
|
|
int r;
|
|
char buf[1024];
|
|
if (sock != INVALID_SOCKET) {
|
|
shutdown(sock, 0x02);
|
|
while (1) {
|
|
r = recv(sock, buf, 1024, 0);
|
|
if ((r == 0) || (r == SOCKET_ERROR)) {
|
|
break;
|
|
}
|
|
}
|
|
closesocket(sock);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#else
|
|
#define fluid_socket_read(_S,_B,_L) read(_S,_B,_L)
|
|
#define fluid_socket_write(_S,_B,_L) write(_S,_B,_L)
|
|
#define SOCKET_ERROR -1
|
|
|
|
void fluid_socket_close(fluid_socket_t sock)
|
|
{
|
|
if (sock != INVALID_SOCKET) {
|
|
close(sock);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(MACINTOSH) && !defined(WIN32)
|
|
|
|
|
|
fluid_istream_t fluid_socket_get_istream(fluid_socket_t sock)
|
|
{
|
|
return sock;
|
|
}
|
|
|
|
fluid_ostream_t fluid_socket_get_ostream(fluid_socket_t sock)
|
|
{
|
|
return sock;
|
|
}
|
|
|
|
|
|
|
|
struct _fluid_server_socket_t {
|
|
fluid_socket_t socket;
|
|
fluid_thread_t* thread;
|
|
int cont;
|
|
fluid_server_func_t func;
|
|
void* data;
|
|
};
|
|
|
|
|
|
static void fluid_server_socket_run(void* data)
|
|
{
|
|
fluid_server_socket_t* server_socket = (fluid_server_socket_t*) data;
|
|
fluid_socket_t client_socket;
|
|
struct sockaddr_in addr;
|
|
socklen_t addrlen = sizeof(addr);
|
|
|
|
FLUID_LOG(FLUID_DBG, "Server listening for connections");
|
|
|
|
while (server_socket->cont) {
|
|
|
|
client_socket = accept(server_socket->socket, (struct sockaddr*) &addr, &addrlen);
|
|
|
|
FLUID_LOG(FLUID_DBG, "New client connection");
|
|
|
|
if (client_socket == INVALID_SOCKET) {
|
|
if (server_socket->cont) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to accept connection");
|
|
}
|
|
server_socket->cont = 0;
|
|
return;
|
|
} else {
|
|
int r;
|
|
r = (*server_socket->func)(server_socket->data, client_socket, inet_ntoa(addr.sin_addr));
|
|
if (r != 0) {
|
|
fluid_socket_close(client_socket);
|
|
}
|
|
}
|
|
}
|
|
|
|
FLUID_LOG(FLUID_DBG, "Server closing");
|
|
}
|
|
|
|
fluid_server_socket_t*
|
|
new_fluid_server_socket(int port, fluid_server_func_t func, void* data)
|
|
{
|
|
fluid_server_socket_t* server_socket;
|
|
struct sockaddr_in addr;
|
|
fluid_socket_t sock;
|
|
|
|
if (func == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Invalid callback function");
|
|
return NULL;
|
|
}
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock == INVALID_SOCKET) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to create server socket");
|
|
return NULL;
|
|
}
|
|
|
|
FLUID_MEMSET((char *)&addr, 0, sizeof(struct sockaddr_in));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
addr.sin_port = htons(port);
|
|
|
|
if (bind(sock, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) {
|
|
FLUID_LOG(FLUID_ERR, "Failed to bind server socket");
|
|
fluid_socket_close(sock);
|
|
return NULL;
|
|
}
|
|
|
|
if (listen(sock, 10) == SOCKET_ERROR) {
|
|
FLUID_LOG(FLUID_ERR, "Failed listen on server socket");
|
|
fluid_socket_close(sock);
|
|
return NULL;
|
|
}
|
|
|
|
server_socket = FLUID_NEW(fluid_server_socket_t);
|
|
if (server_socket == NULL) {
|
|
FLUID_LOG(FLUID_ERR, "Out of memory");
|
|
fluid_socket_close(sock);
|
|
return NULL;
|
|
}
|
|
|
|
server_socket->socket = sock;
|
|
server_socket->func = func;
|
|
server_socket->data = data;
|
|
server_socket->cont = 1;
|
|
|
|
server_socket->thread = new_fluid_thread(fluid_server_socket_run, server_socket, 0);
|
|
if (server_socket->thread == NULL) {
|
|
FLUID_FREE(server_socket);
|
|
fluid_socket_close(sock);
|
|
return NULL;
|
|
}
|
|
|
|
return server_socket;
|
|
}
|
|
|
|
int delete_fluid_server_socket(fluid_server_socket_t* server_socket)
|
|
{
|
|
server_socket->cont = 0;
|
|
if (server_socket->socket != INVALID_SOCKET) {
|
|
fluid_socket_close(server_socket->socket);
|
|
}
|
|
if (server_socket->thread) {
|
|
delete_fluid_thread(server_socket->thread);
|
|
}
|
|
FLUID_FREE(server_socket);
|
|
return FLUID_OK;
|
|
}
|
|
|
|
int fluid_server_socket_join(fluid_server_socket_t* server_socket)
|
|
{
|
|
return fluid_thread_join(server_socket->thread);
|
|
}
|
|
|
|
#endif
|